summaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
commitb4b7ff4b08e691656c9d77c758fc355833128ac0 (patch)
tree82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/s390
parent35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff)
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c1
-rw-r--r--drivers/s390/block/dasd_alias.c5
-rw-r--r--drivers/s390/block/dasd_diag.c2
-rw-r--r--drivers/s390/block/dasd_eckd.c73
-rw-r--r--drivers/s390/block/dcssblk.c8
-rw-r--r--drivers/s390/block/xpram.c5
-rw-r--r--drivers/s390/char/diag_ftp.c2
-rw-r--r--drivers/s390/char/monreader.c4
-rw-r--r--drivers/s390/char/sclp_rw.c136
-rw-r--r--drivers/s390/char/sclp_rw.h17
-rw-r--r--drivers/s390/char/sclp_tty.c8
-rw-r--r--drivers/s390/char/vmlogrdr.c8
-rw-r--r--drivers/s390/cio/chsc.c37
-rw-r--r--drivers/s390/cio/chsc.h15
-rw-r--r--drivers/s390/cio/cio.c47
-rw-r--r--drivers/s390/cio/cmf.c220
-rw-r--r--drivers/s390/cio/css.c7
-rw-r--r--drivers/s390/cio/device.c4
-rw-r--r--drivers/s390/cio/device.h6
-rw-r--r--drivers/s390/cio/device_fsm.c38
-rw-r--r--drivers/s390/cio/device_ops.c107
-rw-r--r--drivers/s390/cio/device_pgid.c70
-rw-r--r--drivers/s390/cio/qdio_main.c4
-rw-r--r--drivers/s390/crypto/Makefile7
-rw-r--r--drivers/s390/crypto/ap_bus.c1818
-rw-r--r--drivers/s390/crypto/ap_bus.h67
-rw-r--r--drivers/s390/crypto/zcrypt_api.c13
-rw-r--r--drivers/s390/crypto/zcrypt_api.h1
-rw-r--r--drivers/s390/crypto/zcrypt_cca_key.h2
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c29
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c62
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c420
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.h115
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c627
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.h174
-rw-r--r--drivers/s390/net/netiucv.c21
-rw-r--r--drivers/s390/net/qeth_core.h30
-rw-r--r--drivers/s390/net/qeth_core_main.c241
-rw-r--r--drivers/s390/net/qeth_core_sys.c43
-rw-r--r--drivers/s390/net/qeth_l2_main.c228
-rw-r--r--drivers/s390/net/qeth_l2_sys.c2
-rw-r--r--drivers/s390/net/qeth_l3_main.c158
-rw-r--r--drivers/s390/net/smsgiucv.c5
-rw-r--r--drivers/s390/virtio/virtio_ccw.c77
44 files changed, 1790 insertions, 3174 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index f73d2f579..a263c1035 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);
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index fe07f3139..184b1dbeb 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);
}
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index c062f1620..cb61f300f 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -21,6 +21,7 @@
#include <asm/dasd.h>
#include <asm/debug.h>
+#include <asm/diag.h>
#include <asm/ebcdic.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -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/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 62a323539..9083247f5 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,20 +1083,10 @@ 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);
- 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)) {
@@ -1090,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
@@ -1147,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;
}
@@ -1159,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
@@ -4423,7 +4440,12 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
/* Read Configuration Data */
- dasd_eckd_read_conf(device);
+ rc = dasd_eckd_read_conf(device);
+ if (rc) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read configuration data failed, rc=%d", rc);
+ goto out_err;
+ }
dasd_eckd_get_uid(device, &temp_uid);
/* Generate device unique id */
@@ -4439,13 +4461,18 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
/* register lcu with alias handling, enable PAV if this is a new lcu */
rc = dasd_alias_make_device_known_to_lcu(device);
if (rc)
- return rc;
+ goto out_err;
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr_flags);
dasd_eckd_validate_server(device, cqr_flags);
/* RE-Read Configuration Data */
- dasd_eckd_read_conf(device);
+ rc = dasd_eckd_read_conf(device);
+ if (rc) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+ "Read configuration data failed, rc=%d", rc);
+ goto out_err2;
+ }
/* Read Feature Codes */
dasd_eckd_read_features(device);
@@ -4456,7 +4483,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
if (rc) {
DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
"Read device characteristic failed, rc=%d", rc);
- goto out_err;
+ goto out_err2;
}
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data));
@@ -4467,6 +4494,8 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
return 0;
+out_err2:
+ dasd_alias_disconnect_device_from_lcu(device);
out_err:
return -1;
}
@@ -4671,7 +4700,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 +4745,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 +4966,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/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 5ed44fe21..94a8f4ab5 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -27,7 +27,8 @@
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
static void dcssblk_release(struct gendisk *disk, fmode_t mode);
-static void dcssblk_make_request(struct request_queue *q, struct bio *bio);
+static blk_qc_t dcssblk_make_request(struct request_queue *q,
+ struct bio *bio);
static long dcssblk_direct_access(struct block_device *bdev, sector_t secnum,
void __pmem **kaddr, unsigned long *pfn);
@@ -815,7 +816,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
up_write(&dcssblk_devices_sem);
}
-static void
+static blk_qc_t
dcssblk_make_request(struct request_queue *q, struct bio *bio)
{
struct dcssblk_dev_info *dev_info;
@@ -874,9 +875,10 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
bytes_done += bvec.bv_len;
}
bio_endio(bio);
- return;
+ return BLK_QC_T_NONE;
fail:
bio_io_error(bio);
+ return BLK_QC_T_NONE;
}
static long
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 02871f1db..288f59a41 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -181,7 +181,7 @@ static unsigned long xpram_highest_page_index(void)
/*
* Block device make request function.
*/
-static void xpram_make_request(struct request_queue *q, struct bio *bio)
+static blk_qc_t xpram_make_request(struct request_queue *q, struct bio *bio)
{
xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data;
struct bio_vec bvec;
@@ -223,9 +223,10 @@ static void xpram_make_request(struct request_queue *q, struct bio *bio)
}
}
bio_endio(bio);
- return;
+ return BLK_QC_T_NONE;
fail:
bio_io_error(bio);
+ return BLK_QC_T_NONE;
}
static int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo)
diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c
index 12db8db04..a5ccbf6f0 100644
--- a/drivers/s390/char/diag_ftp.c
+++ b/drivers/s390/char/diag_ftp.c
@@ -15,6 +15,7 @@
#include <linux/wait.h>
#include <linux/string.h>
#include <asm/ctl_reg.h>
+#include <asm/diag.h>
#include "hmcdrv_ftp.h"
#include "diag_ftp.h"
@@ -102,6 +103,7 @@ static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
{
int rc;
+ diag_stat_inc(DIAG_STAT_X2C4);
asm volatile(
" diag %[addr],%[cmd],0x2c4\n"
"0: j 2f\n"
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index b7d60306b..fc94bfdce 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -229,7 +229,7 @@ static struct mon_msg *mon_next_message(struct mon_private *monpriv)
/******************************************************************************
* IUCV handler *
*****************************************************************************/
-static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
{
struct mon_private *monpriv = path->private;
@@ -237,7 +237,7 @@ static void mon_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
wake_up(&mon_conn_wait_queue);
}
-static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
{
struct mon_private *monpriv = path->private;
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 35a84af87..6010cd347 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 7a7bfc947..e3b029099 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 003663288..3c6e174e1 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;
}
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 9bb48d709..799c1524c 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -99,8 +99,8 @@ static const struct file_operations vmlogrdr_fops = {
};
-static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 ipuser[16]);
-static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 ipuser[16]);
+static void vmlogrdr_iucv_path_complete(struct iucv_path *, u8 *ipuser);
+static void vmlogrdr_iucv_path_severed(struct iucv_path *, u8 *ipuser);
static void vmlogrdr_iucv_message_pending(struct iucv_path *,
struct iucv_message *);
@@ -160,7 +160,7 @@ static struct cdev *vmlogrdr_cdev = NULL;
static int recording_class_AB;
-static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
{
struct vmlogrdr_priv_t * logptr = path->private;
@@ -171,7 +171,7 @@ static void vmlogrdr_iucv_path_complete(struct iucv_path *path, u8 ipuser[16])
}
-static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
{
struct vmlogrdr_priv_t * logptr = path->private;
u8 reason = (u8) ipuser[8];
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 548a18916..a831d1859 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -1080,28 +1080,10 @@ void __init chsc_init_cleanup(void)
free_page((unsigned long)sei_page);
}
-int chsc_enable_facility(int operation_code)
+int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)
{
- unsigned long flags;
int ret;
- struct {
- struct chsc_header request;
- u8 reserved1:4;
- u8 format:4;
- u8 reserved2;
- u16 operation_code;
- u32 reserved3;
- u32 reserved4;
- u32 operation_data_area[252];
- struct chsc_header response;
- u32 reserved5:4;
- u32 format2:4;
- u32 reserved6:24;
- } __attribute__ ((packed)) *sda_area;
- spin_lock_irqsave(&chsc_page_lock, flags);
- memset(chsc_page, 0, PAGE_SIZE);
- sda_area = chsc_page;
sda_area->request.length = 0x0400;
sda_area->request.code = 0x0031;
sda_area->operation_code = operation_code;
@@ -1119,10 +1101,25 @@ int chsc_enable_facility(int operation_code)
default:
ret = chsc_error_from_response(sda_area->response.code);
}
+out:
+ return ret;
+}
+
+int chsc_enable_facility(int operation_code)
+{
+ struct chsc_sda_area *sda_area;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&chsc_page_lock, flags);
+ memset(chsc_page, 0, PAGE_SIZE);
+ sda_area = chsc_page;
+
+ ret = __chsc_enable_facility(sda_area, operation_code);
if (ret != 0)
CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n",
operation_code, sda_area->response.code);
-out:
+
spin_unlock_irqrestore(&chsc_page_lock, flags);
return ret;
}
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 76c9b5070..0de134c3a 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -115,6 +115,20 @@ struct chsc_scpd {
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed));
+struct chsc_sda_area {
+ struct chsc_header request;
+ u8 :4;
+ u8 format:4;
+ u8 :8;
+ u16 operation_code;
+ u32 :32;
+ u32 :32;
+ u32 operation_data_area[252];
+ struct chsc_header response;
+ u32 :4;
+ u32 format2:4;
+ u32 :24;
+} __packed __aligned(PAGE_SIZE);
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
@@ -122,6 +136,7 @@ extern int chsc_determine_css_characteristics(void);
extern int chsc_init(void);
extern void chsc_init_cleanup(void);
+int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code);
extern int chsc_enable_facility(int);
struct channel_subsystem;
extern int chsc_secm(struct channel_subsystem *, int);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 07fc5d9e7..690b8547e 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;
@@ -826,11 +807,11 @@ static atomic_t chpid_reset_count;
static void s390_reset_chpids_mcck_handler(void)
{
struct crw crw;
- struct mci *mci;
+ union mci mci;
/* Check for pending channel report word. */
- mci = (struct mci *)&S390_lowcore.mcck_interruption_code;
- if (!mci->cp)
+ mci.val = S390_lowcore.mcck_interruption_code;
+ if (!mci.cp)
return;
/* Process channel report words. */
while (stcrw(&crw) == 0) {
@@ -944,18 +925,32 @@ void reipl_ccw_dev(struct ccw_dev_id *devid)
int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
{
+ static struct chsc_sda_area sda_area __initdata;
struct subchannel_id schid;
struct schib schib;
schid = *(struct subchannel_id *)&S390_lowcore.subchannel_id;
if (!schid.one)
return -ENODEV;
+
+ if (schid.ssid) {
+ /*
+ * Firmware should have already enabled MSS but whoever started
+ * the kernel might have initiated a channel subsystem reset.
+ * Ensure that MSS is enabled.
+ */
+ memset(&sda_area, 0, sizeof(sda_area));
+ if (__chsc_enable_facility(&sda_area, CHSC_SDA_OC_MSS))
+ return -ENODEV;
+ }
if (stsch_err(schid, &schib))
return -ENODEV;
if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
return -ENODEV;
if (!schib.pmcw.dnv)
return -ENODEV;
+
+ iplinfo->ssid = schid.ssid;
iplinfo->devno = schib.pmcw.dev;
iplinfo->is_qdio = schib.pmcw.qf;
return 0;
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 23054f8fa..b2afad5a5 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;
};
@@ -186,9 +184,8 @@ static inline void cmf_activate(void *area, unsigned int onoff)
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
unsigned long address)
{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ int ret;
sch->config.mme = mme;
sch->config.mbfc = mbfc;
@@ -198,7 +195,15 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
else
sch->config.mbi = address;
- return cio_commit_config(sch);
+ ret = cio_commit_config(sch);
+ if (!mme && ret == -ENODEV) {
+ /*
+ * The task was to disable measurement block updates but
+ * the subchannel is already gone. Report success.
+ */
+ ret = 0;
+ }
+ return ret;
}
struct set_schib_struct {
@@ -314,7 +319,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
return -EBUSY;
}
cmb_data = cdev->private->cmb;
- hw_block = cmbops->align(cmb_data->hw_block);
+ hw_block = cmb_data->hw_block;
if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size))
/* No need to copy. */
return 0;
@@ -425,7 +430,7 @@ static void cmf_generic_reset(struct ccw_device *cdev)
* Need to reset hw block as well to make the hardware start
* from 0 again.
*/
- memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);
+ memset(cmb_data->hw_block, 0, cmb_data->size);
cmb_data->last_update = 0;
}
cdev->private->cmb_start_time = get_tod_clock();
@@ -606,12 +611,6 @@ static void free_cmb(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock);
priv = cdev->private;
-
- if (list_empty(&priv->cmb_list)) {
- /* already freed */
- goto out;
- }
-
cmb_data = priv->cmb;
priv->cmb = NULL;
if (cmb_data)
@@ -626,7 +625,6 @@ static void free_cmb(struct ccw_device *cdev)
free_pages((unsigned long)cmb_area.mem, get_order(size));
cmb_area.mem = NULL;
}
-out:
spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
@@ -755,11 +753,6 @@ static void reset_cmb(struct ccw_device *cdev)
cmf_generic_reset(cdev);
}
-static void * align_cmb(void *area)
-{
- return area;
-}
-
static struct attribute_group cmf_attr_group;
static struct cmb_operations cmbops_basic = {
@@ -769,7 +762,6 @@ static struct cmb_operations cmbops_basic = {
.read = read_cmb,
.readall = readall_cmb,
.reset = reset_cmb,
- .align = align_cmb,
.attr_group = &cmf_attr_group,
};
@@ -804,64 +796,57 @@ struct cmbe {
u32 device_busy_time;
u32 initial_command_response_time;
u32 reserved[7];
-};
+} __packed __aligned(64);
-/*
- * kmalloc only guarantees 8 byte alignment, but we need cmbe
- * pointers to be naturally aligned. Make sure to allocate
- * enough space for two cmbes.
- */
-static inline struct cmbe *cmbe_align(struct cmbe *c)
-{
- unsigned long addr;
- addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) &
- ~(sizeof (struct cmbe) - sizeof(long));
- return (struct cmbe*)addr;
-}
+static struct kmem_cache *cmbe_cache;
static int alloc_cmbe(struct ccw_device *cdev)
{
- struct cmbe *cmbe;
struct cmb_data *cmb_data;
- int ret;
+ struct cmbe *cmbe;
+ int ret = -ENOMEM;
- cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
+ cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL);
if (!cmbe)
- return -ENOMEM;
- cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
- if (!cmb_data) {
- ret = -ENOMEM;
+ return ret;
+
+ cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL);
+ if (!cmb_data)
goto out_free;
- }
+
cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);
- if (!cmb_data->last_block) {
- ret = -ENOMEM;
+ if (!cmb_data->last_block)
goto out_free;
- }
- cmb_data->size = sizeof(struct cmbe);
- spin_lock_irq(cdev->ccwlock);
- if (cdev->private->cmb) {
- spin_unlock_irq(cdev->ccwlock);
- ret = -EBUSY;
- goto out_free;
- }
+
+ cmb_data->size = sizeof(*cmbe);
cmb_data->hw_block = cmbe;
+
+ spin_lock(&cmb_area.lock);
+ spin_lock_irq(cdev->ccwlock);
+ if (cdev->private->cmb)
+ goto out_unlock;
+
cdev->private->cmb = cmb_data;
- spin_unlock_irq(cdev->ccwlock);
/* activate global measurement if this is the first channel */
- spin_lock(&cmb_area.lock);
if (list_empty(&cmb_area.list))
cmf_activate(NULL, 1);
list_add_tail(&cdev->private->cmb_list, &cmb_area.list);
- spin_unlock(&cmb_area.lock);
+ spin_unlock_irq(cdev->ccwlock);
+ spin_unlock(&cmb_area.lock);
return 0;
+
+out_unlock:
+ spin_unlock_irq(cdev->ccwlock);
+ spin_unlock(&cmb_area.lock);
+ ret = -EBUSY;
out_free:
if (cmb_data)
kfree(cmb_data->last_block);
kfree(cmb_data);
- kfree(cmbe);
+ kmem_cache_free(cmbe_cache, cmbe);
+
return ret;
}
@@ -869,19 +854,21 @@ static void free_cmbe(struct ccw_device *cdev)
{
struct cmb_data *cmb_data;
+ spin_lock(&cmb_area.lock);
spin_lock_irq(cdev->ccwlock);
cmb_data = cdev->private->cmb;
cdev->private->cmb = NULL;
- if (cmb_data)
+ if (cmb_data) {
kfree(cmb_data->last_block);
+ kmem_cache_free(cmbe_cache, cmb_data->hw_block);
+ }
kfree(cmb_data);
- spin_unlock_irq(cdev->ccwlock);
/* deactivate global measurement if this is the last channel */
- spin_lock(&cmb_area.lock);
list_del_init(&cdev->private->cmb_list);
if (list_empty(&cmb_area.list))
cmf_activate(NULL, 0);
+ spin_unlock_irq(cdev->ccwlock);
spin_unlock(&cmb_area.lock);
}
@@ -897,7 +884,7 @@ static int set_cmbe(struct ccw_device *cdev, u32 mme)
return -EINVAL;
}
cmb_data = cdev->private->cmb;
- mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0;
+ mba = mme ? (unsigned long) cmb_data->hw_block : 0;
spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 1, mba);
@@ -1022,11 +1009,6 @@ static void reset_cmbe(struct ccw_device *cdev)
cmf_generic_reset(cdev);
}
-static void * align_cmbe(void *area)
-{
- return cmbe_align(area);
-}
-
static struct attribute_group cmf_attr_group_ext;
static struct cmb_operations cmbops_extended = {
@@ -1036,7 +1018,6 @@ static struct cmb_operations cmbops_extended = {
.read = read_cmbe,
.readall = readall_cmbe,
.reset = reset_cmbe,
- .align = align_cmbe,
.attr_group = &cmf_attr_group_ext,
};
@@ -1171,23 +1152,28 @@ static ssize_t cmb_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0);
+ struct ccw_device *cdev = to_ccwdev(dev);
+ int enabled;
+
+ spin_lock_irq(cdev->ccwlock);
+ enabled = !!cdev->private->cmb;
+ spin_unlock_irq(cdev->ccwlock);
+
+ return sprintf(buf, "%d\n", enabled);
}
static ssize_t cmb_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t c)
{
- struct ccw_device *cdev;
- int ret;
+ struct ccw_device *cdev = to_ccwdev(dev);
unsigned long val;
+ int ret;
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
- cdev = to_ccwdev(dev);
-
switch (val) {
case 0:
ret = disable_cmf(cdev);
@@ -1195,12 +1181,13 @@ static ssize_t cmb_enable_store(struct device *dev,
case 1:
ret = enable_cmf(cdev);
break;
+ default:
+ ret = -EINVAL;
}
- return c;
+ return ret ? ret : c;
}
-
-DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);
+DEVICE_ATTR_RW(cmb_enable);
int ccw_set_cmf(struct ccw_device *cdev, int enable)
{
@@ -1220,41 +1207,71 @@ int enable_cmf(struct ccw_device *cdev)
{
int ret;
+ device_lock(&cdev->dev);
+ get_device(&cdev->dev);
ret = cmbops->alloc(cdev);
- cmbops->reset(cdev);
if (ret)
- return ret;
+ goto out;
+ cmbops->reset(cdev);
+ ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
+ if (ret) {
+ cmbops->free(cdev);
+ goto out;
+ }
ret = cmbops->set(cdev, 2);
if (ret) {
+ sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
cmbops->free(cdev);
- return ret;
}
- ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
- if (!ret)
- return 0;
- cmbops->set(cdev, 0); //FIXME: this can fail
- cmbops->free(cdev);
+out:
+ if (ret)
+ put_device(&cdev->dev);
+
+ device_unlock(&cdev->dev);
return ret;
}
/**
- * disable_cmf() - switch off the channel measurement for a specific device
+ * __disable_cmf() - switch off the channel measurement for a specific device
* @cdev: The ccw device to be disabled
*
* Returns %0 for success or a negative error value.
*
* Context:
- * non-atomic
+ * non-atomic, device_lock() held.
*/
-int disable_cmf(struct ccw_device *cdev)
+int __disable_cmf(struct ccw_device *cdev)
{
int ret;
ret = cmbops->set(cdev, 0);
if (ret)
return ret;
- cmbops->free(cdev);
+
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
+ cmbops->free(cdev);
+ put_device(&cdev->dev);
+
+ return ret;
+}
+
+/**
+ * disable_cmf() - switch off the channel measurement for a specific device
+ * @cdev: The ccw device to be disabled
+ *
+ * Returns %0 for success or a negative error value.
+ *
+ * Context:
+ * non-atomic
+ */
+int disable_cmf(struct ccw_device *cdev)
+{
+ int ret;
+
+ device_lock(&cdev->dev);
+ ret = __disable_cmf(cdev);
+ device_unlock(&cdev->dev);
+
return ret;
}
@@ -1295,10 +1312,32 @@ int cmf_reenable(struct ccw_device *cdev)
return cmbops->set(cdev, 2);
}
+/**
+ * cmf_reactivate() - reactivate measurement block updates
+ *
+ * Use this during resume from hibernate.
+ */
+void cmf_reactivate(void)
+{
+ spin_lock(&cmb_area.lock);
+ if (!list_empty(&cmb_area.list))
+ cmf_activate(cmb_area.mem, 1);
+ spin_unlock(&cmb_area.lock);
+}
+
+static int __init init_cmbe(void)
+{
+ cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe),
+ __alignof__(struct cmbe), 0, NULL);
+
+ return cmbe_cache ? 0 : -ENOMEM;
+}
+
static int __init init_cmf(void)
{
char *format_string;
- char *detect_string = "parameter";
+ char *detect_string;
+ int ret;
/*
* If the user did not give a parameter, see if we are running on a
@@ -1324,15 +1363,18 @@ static int __init init_cmf(void)
case CMF_EXTENDED:
format_string = "extended";
cmbops = &cmbops_extended;
+
+ ret = init_cmbe();
+ if (ret)
+ return ret;
break;
default:
- return 1;
+ return -EINVAL;
}
pr_info("Channel measurement facility initialized using format "
"%s (mode %s)\n", format_string, detect_string);
return 0;
}
-
module_init(init_cmf);
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 0268e5fd5..489e703dc 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);
@@ -703,17 +702,12 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
} else {
-#ifdef CONFIG_SMP
css->global_pgid.pgid_high.cpu_addr = stap();
-#else
- css->global_pgid.pgid_high.cpu_addr = 0;
-#endif
}
get_cpu_id(&cpu_id);
css->global_pgid.cpu_id = cpu_id.ident;
css->global_pgid.cpu_model = cpu_id.machine;
css->global_pgid.tod_high = tod_high;
-
}
static void
@@ -1089,6 +1083,7 @@ void channel_subsystem_reinit(void)
if (chp)
chp_update_desc(chp);
}
+ cmf_reactivate();
}
#ifdef CONFIG_PROC_FS
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index dfef5e63c..6aae68412 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;
}
@@ -1797,7 +1799,7 @@ static void ccw_device_shutdown(struct device *dev)
cdev = to_ccwdev(dev);
if (cdev->drv && cdev->drv->shutdown)
cdev->drv->shutdown(cdev);
- disable_cmf(cdev);
+ __disable_cmf(cdev);
}
static int ccw_device_pm_prepare(struct device *dev)
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 8d1d29873..065b1be98 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -125,11 +125,6 @@ void ccw_device_verify_done(struct ccw_device *, int);
void ccw_device_disband_start(struct ccw_device *);
void ccw_device_disband_done(struct ccw_device *, int);
-void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
-void ccw_device_stlck_done(struct ccw_device *, void *, int);
-
-int ccw_device_call_handler(struct ccw_device *);
-
int ccw_device_stlck(struct ccw_device *);
/* Helper function for machine check handling. */
@@ -145,6 +140,7 @@ void ccw_device_set_timeout(struct ccw_device *, int);
void retry_set_schib(struct ccw_device *cdev);
void cmf_retry_copy_block(struct ccw_device *);
int cmf_reenable(struct ccw_device *);
+void cmf_reactivate(void);
int ccw_set_cmf(struct ccw_device *cdev, int enable);
extern struct device_attribute dev_attr_cmb_enable;
#endif
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 83da53c8e..92e03b42e 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -731,6 +731,44 @@ static void ccw_device_boxed_verify(struct ccw_device *cdev,
}
/*
+ * Pass interrupt to device driver.
+ */
+static int ccw_device_call_handler(struct ccw_device *cdev)
+{
+ unsigned int stctl;
+ int ending_status;
+
+ /*
+ * we allow for the device action handler if .
+ * - we received ending status
+ * - the action handler requested to see all interrupts
+ * - we received an intermediate status
+ * - fast notification was requested (primary status)
+ * - unsolicited interrupts
+ */
+ stctl = scsw_stctl(&cdev->private->irb.scsw);
+ ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
+ (stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
+ (stctl == SCSW_STCTL_STATUS_PEND);
+ if (!ending_status &&
+ !cdev->private->options.repall &&
+ !(stctl & SCSW_STCTL_INTER_STATUS) &&
+ !(cdev->private->options.fast &&
+ (stctl & SCSW_STCTL_PRIM_STATUS)))
+ return 0;
+
+ if (ending_status)
+ ccw_device_set_timeout(cdev, 0);
+
+ if (cdev->handler)
+ cdev->handler(cdev, cdev->private->intparm,
+ &cdev->private->irb);
+
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+ return 1;
+}
+
+/*
* Got an interrupt for a normal io (state online).
*/
static void
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 6acd0b577..a69f702a2 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
@@ -502,67 +456,6 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
return sch->lpm;
}
-struct stlck_data {
- struct completion done;
- int rc;
-};
-
-void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
-{
- struct stlck_data *sdata = data;
-
- sdata->rc = rc;
- complete(&sdata->done);
-}
-
-/*
- * Perform unconditional reserve + release.
- */
-int ccw_device_stlck(struct ccw_device *cdev)
-{
- struct subchannel *sch = to_subchannel(cdev->dev.parent);
- struct stlck_data data;
- u8 *buffer;
- int rc;
-
- /* Check if steal lock operation is valid for this device. */
- if (cdev->drv) {
- if (!cdev->private->options.force)
- return -EINVAL;
- }
- buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- init_completion(&data.done);
- data.rc = -EIO;
- spin_lock_irq(sch->lock);
- rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
- if (rc)
- goto out_unlock;
- /* Perform operation. */
- cdev->private->state = DEV_STATE_STEAL_LOCK;
- ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
- spin_unlock_irq(sch->lock);
- /* Wait for operation to finish. */
- if (wait_for_completion_interruptible(&data.done)) {
- /* Got a signal. */
- spin_lock_irq(sch->lock);
- ccw_request_cancel(cdev);
- spin_unlock_irq(sch->lock);
- wait_for_completion(&data.done);
- }
- rc = data.rc;
- /* Check results. */
- spin_lock_irq(sch->lock);
- cio_disable_subchannel(sch);
- cdev->private->state = DEV_STATE_BOXED;
-out_unlock:
- spin_unlock_irq(sch->lock);
- kfree(buffer);
-
- return rc;
-}
-
/**
* chp_get_chp_desc - return newly allocated channel-path descriptor
* @cdev: device to obtain the descriptor for
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 37ada05e8..da246b67e 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -9,9 +9,10 @@
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/errno.h>
-#include <linux/bitops.h>
+#include <linux/slab.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -133,7 +134,7 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn)
{
struct ccw_request *req = &cdev->private->req;
struct ccw1 *cp = cdev->private->iccws;
- int i = 8 - ffs(req->lpm);
+ int i = pathmask_to_pos(req->lpm);
struct pgid *pgid = &cdev->private->pgid[i];
pgid->inf.fc = fn;
@@ -434,7 +435,7 @@ static void snid_build_cp(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
struct ccw1 *cp = cdev->private->iccws;
- int i = 8 - ffs(req->lpm);
+ int i = pathmask_to_pos(req->lpm);
/* Channel program setup. */
cp->cmd_code = CCW_CMD_SENSE_PGID;
@@ -616,6 +617,11 @@ void ccw_device_disband_start(struct ccw_device *cdev)
ccw_request_start(cdev);
}
+struct stlck_data {
+ struct completion done;
+ int rc;
+};
+
static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
{
struct ccw_request *req = &cdev->private->req;
@@ -634,7 +640,10 @@ static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
{
- ccw_device_stlck_done(cdev, data, rc);
+ struct stlck_data *sdata = data;
+
+ sdata->rc = rc;
+ complete(&sdata->done);
}
/**
@@ -645,11 +654,9 @@ static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
* @buf2: data pointer used in channel program
*
* Execute a channel program on @cdev to release an existing PGID reservation.
- * When finished, call ccw_device_stlck_done with a return code specifying the
- * result.
*/
-void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
- void *buf2)
+static void ccw_device_stlck_start(struct ccw_device *cdev, void *data,
+ void *buf1, void *buf2)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
@@ -667,3 +674,50 @@ void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
ccw_request_start(cdev);
}
+/*
+ * Perform unconditional reserve + release.
+ */
+int ccw_device_stlck(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct stlck_data data;
+ u8 *buffer;
+ int rc;
+
+ /* Check if steal lock operation is valid for this device. */
+ if (cdev->drv) {
+ if (!cdev->private->options.force)
+ return -EINVAL;
+ }
+ buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ init_completion(&data.done);
+ data.rc = -EIO;
+ spin_lock_irq(sch->lock);
+ rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
+ if (rc)
+ goto out_unlock;
+ /* Perform operation. */
+ cdev->private->state = DEV_STATE_STEAL_LOCK;
+ ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
+ spin_unlock_irq(sch->lock);
+ /* Wait for operation to finish. */
+ if (wait_for_completion_interruptible(&data.done)) {
+ /* Got a signal. */
+ spin_lock_irq(sch->lock);
+ ccw_request_cancel(cdev);
+ spin_unlock_irq(sch->lock);
+ wait_for_completion(&data.done);
+ }
+ rc = data.rc;
+ /* Check results. */
+ spin_lock_irq(sch->lock);
+ cio_disable_subchannel(sch);
+ cdev->private->state = DEV_STATE_BOXED;
+out_unlock:
+ spin_unlock_irq(sch->lock);
+ kfree(buffer);
+
+ return rc;
+}
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 848e3b64e..4bb5262f7 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -319,6 +319,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
int retries = 0, cc;
unsigned long laob = 0;
+ WARN_ON_ONCE(aob && ((queue_type(q) != QDIO_IQDIO_QFMT) ||
+ !q->u.out.use_cq));
if (q->u.out.use_cq && aob != 0) {
fc = QDIO_SIGA_WRITEQ;
laob = aob;
@@ -329,8 +331,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit,
fc |= QDIO_SIGA_QEBSM_FLAG;
}
again:
- WARN_ON_ONCE((aob && queue_type(q) != QDIO_IQDIO_QFMT) ||
- (aob && fc != QDIO_SIGA_WRITEQ));
cc = do_siga_output(schid, q->mask, busy_bit, fc, laob);
/* hipersocket busy condition */
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index 771faf709..b8ab18676 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -3,6 +3,9 @@
#
ap-objs := ap_bus.o
-obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
-obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o zcrypt_cex4.o
+# zcrypt_api depends on ap
+obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o
+# msgtype* depend on zcrypt_api
obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o
+# adapter drivers depend on ap, zcrypt_api and msgtype*
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index d78b3d629..24ec282e1 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -37,6 +37,7 @@
#include <linux/notifier.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
+#include <linux/suspend.h>
#include <asm/reset.h>
#include <asm/airq.h>
#include <linux/atomic.h>
@@ -48,23 +49,6 @@
#include "ap_bus.h"
-/* Some prototypes. */
-static void ap_scan_bus(struct work_struct *);
-static void ap_poll_all(unsigned long);
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
-static int ap_poll_thread_start(void);
-static void ap_poll_thread_stop(void);
-static void ap_request_timeout(unsigned long);
-static inline void ap_schedule_poll_timer(void);
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
-static int ap_device_remove(struct device *dev);
-static int ap_device_probe(struct device *dev);
-static void ap_interrupt_handler(struct airq_struct *airq);
-static void ap_reset(struct ap_device *ap_dev, 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.
*/
@@ -90,19 +74,21 @@ static struct device *ap_root_device = NULL;
static struct ap_config_info *ap_configuration;
static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
+static bool initialised;
/*
- * Workqueue & timer for bus rescan.
+ * Workqueue timer for bus rescan.
*/
-static struct workqueue_struct *ap_work_queue;
static struct timer_list ap_config_timer;
static int ap_config_time = AP_CONFIG_TIME;
-static DECLARE_WORK(ap_config_work, ap_scan_bus);
+static void ap_scan_bus(struct work_struct *);
+static DECLARE_WORK(ap_scan_work, ap_scan_bus);
/*
* Tasklet & timer for AP request polling and interrupts
*/
-static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
+static void ap_tasklet_fn(unsigned long);
+static DECLARE_TASKLET(ap_tasklet, ap_tasklet_fn, 0);
static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
@@ -115,6 +101,8 @@ static unsigned long long poll_timeout = 250000;
/* Suspend flag */
static int ap_suspend_flag;
+/* Maximum domain id */
+static int ap_max_domain_id;
/* Flag to check if domain was set through module parameter domain=. This is
* important when supsend and resume is done in a z/VM environment where the
* domain might change. */
@@ -122,6 +110,8 @@ static int user_set_domain = 0;
static struct bus_type ap_bus_type;
/* Adapter interrupt definitions */
+static void ap_interrupt_handler(struct airq_struct *airq);
+
static int ap_airq_flag;
static struct airq_struct ap_airq = {
@@ -182,44 +172,27 @@ static int ap_configuration_available(void)
/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
- * @queue_depth: Pointer to queue depth value
- * @device_type: Pointer to device type value
+ * @info: Pointer to queue descriptor
*
* Returns AP queue status structure.
*/
static inline struct ap_queue_status
-ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+ap_test_queue(ap_qid_t qid, unsigned long *info)
{
register unsigned long reg0 asm ("0") = qid;
register struct ap_queue_status reg1 asm ("1");
register unsigned long reg2 asm ("2") = 0UL;
+ if (test_facility(15))
+ reg0 |= 1UL << 23; /* set APFT T bit*/
asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */
: "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
- *device_type = (int) (reg2 >> 24);
- *queue_depth = (int) (reg2 & 0xff);
+ if (info)
+ *info = reg2;
return reg1;
}
/**
- * ap_query_facilities(): PQAP(TAPQ) query facilities.
- * @qid: The AP queue number
- *
- * Returns content of general register 2 after the PQAP(TAPQ)
- * instruction was called.
- */
-static inline unsigned long ap_query_facilities(ap_qid_t qid)
-{
- register unsigned long reg0 asm ("0") = qid | 0x00800000UL;
- register unsigned long reg1 asm ("1");
- register unsigned long reg2 asm ("2") = 0UL;
-
- asm volatile(".long 0xb2af0000" /* PQAP(TAPQ) */
- : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
- return reg2;
-}
-
-/**
* ap_reset_queue(): Reset adjunct processor queue.
* @qid: The AP queue number
*
@@ -259,31 +232,19 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind)
return reg1_out;
}
-static inline struct ap_queue_status
-__ap_query_functions(ap_qid_t qid, unsigned int *functions)
-{
- register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23);
- register struct ap_queue_status reg1 asm ("1") = AP_QUEUE_STATUS_INVALID;
- register unsigned long reg2 asm ("2");
-
- asm volatile(
- ".long 0xb2af0000\n" /* PQAP(TAPQ) */
- "0:\n"
- EX_TABLE(0b, 0b)
- : "+d" (reg0), "+d" (reg1), "=d" (reg2)
- :
- : "cc");
-
- *functions = (unsigned int)(reg2 >> 32);
- return reg1;
-}
-
-static inline int __ap_query_configuration(struct ap_config_info *config)
+/**
+ * ap_query_configuration(): Get AP configuration data
+ *
+ * Returns 0 on success, or -EOPNOTSUPP.
+ */
+static inline int ap_query_configuration(void)
{
register unsigned long reg0 asm ("0") = 0x04000000UL;
register unsigned long reg1 asm ("1") = -EINVAL;
- register unsigned char *reg2 asm ("2") = (unsigned char *)config;
+ register void *reg2 asm ("2") = (void *) ap_configuration;
+ if (!ap_configuration)
+ return -EOPNOTSUPP;
asm volatile(
".long 0xb2af0000\n" /* PQAP(QCI) */
"0: la %1,0\n"
@@ -297,39 +258,60 @@ static inline int __ap_query_configuration(struct ap_config_info *config)
}
/**
- * ap_query_functions(): Query supported functions.
- * @qid: The AP queue number
- * @functions: Pointer to functions field.
- *
- * Returns
- * 0 on success.
- * -ENODEV if queue not valid.
- * -EBUSY if device busy.
- * -EINVAL if query function is not supported
+ * ap_init_configuration(): Allocate and query configuration array.
*/
-static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
+static void ap_init_configuration(void)
{
- struct ap_queue_status status;
+ if (!ap_configuration_available())
+ return;
- status = __ap_query_functions(qid, functions);
+ ap_configuration = kzalloc(sizeof(*ap_configuration), GFP_KERNEL);
+ if (!ap_configuration)
+ return;
+ if (ap_query_configuration() != 0) {
+ kfree(ap_configuration);
+ ap_configuration = NULL;
+ return;
+ }
+}
- if (ap_queue_status_invalid_test(&status))
- return -ENODEV;
+/*
+ * 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));
+}
- 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_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);
}
/**
@@ -354,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:
@@ -480,137 +464,562 @@ 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
+ * ap_query_queue(): Check if an AP queue is available.
+ * @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 inline void __ap_schedule_poll_timer(void)
+static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
+ unsigned int *facilities)
+{
+ 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) {
+ case AP_RESPONSE_NORMAL:
+ *queue_depth = (int)(info & 0xff);
+ *device_type = (int)((info >> 24) & 0xff);
+ *facilities = (unsigned int)(info >> 32);
+ /* Update maximum domain id */
+ nd = (info >> 16) & 0xff;
+ if ((info & (1UL << 57)) && nd > 0)
+ ap_max_domain_id = nd;
+ return 0;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ case AP_RESPONSE_INVALID_ADDRESS:
+ return -ENODEV;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ case AP_RESPONSE_BUSY:
+ return -EBUSY;
+ default:
+ BUG();
+ }
+}
+
+/* State machine definitions and helpers */
+
+static void ap_sm_wait(enum ap_wait wait)
{
ktime_t hr_time;
- 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);
+ 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;
}
- spin_unlock_bh(&ap_poll_timer_lock);
+}
+
+static enum ap_wait ap_sm_nop(struct ap_device *ap_dev)
+{
+ return AP_WAIT_NONE;
}
/**
- * ap_schedule_poll_timer(): Schedule poll timer.
+ * 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
*
- * Set up the timer to run the poll tasklet
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
*/
-static inline void ap_schedule_poll_timer(void)
+static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev)
{
- if (ap_using_interrupts())
- return;
- __ap_schedule_poll_timer();
+ 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) {
+ ap_dev->state = AP_STATE_WORKING;
+ return AP_WAIT_AGAIN;
+ }
+ ap_dev->state = AP_STATE_IDLE;
+ return AP_WAIT_NONE;
+ case AP_RESPONSE_NO_PENDING_REPLY:
+ if (ap_dev->queue_count > 0)
+ return AP_WAIT_INTERRUPT;
+ ap_dev->state = AP_STATE_IDLE;
+ return AP_WAIT_NONE;
+ default:
+ ap_dev->state = AP_STATE_BORKED;
+ return AP_WAIT_NONE;
+ }
+}
/**
- * ap_query_queue(): Check if an AP queue is available.
- * @qid: The AP queue number
- * @queue_depth: Pointer to queue depth value
- * @device_type: Pointer to device type value
+ * 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 int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+static enum ap_wait ap_sm_write(struct ap_device *ap_dev)
{
struct ap_queue_status status;
- int t_depth, t_device_type;
+ struct ap_message *ap_msg;
- status = ap_test_queue(qid, &t_depth, &t_device_type);
+ 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:
- *queue_depth = t_depth + 1;
- *device_type = t_device_type;
- return 0;
- case AP_RESPONSE_Q_NOT_AVAIL:
- case AP_RESPONSE_DECONFIGURED:
- case AP_RESPONSE_CHECKSTOPPED:
- case AP_RESPONSE_INVALID_ADDRESS:
- return -ENODEV;
+ 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:
- case AP_RESPONSE_OTHERWISE_CHANGED:
- case AP_RESPONSE_BUSY:
- return -EBUSY;
+ 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:
- BUG();
+ 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;
+
+ 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);
- ap_dev->queue_count++;
- if (ap_dev->queue_count == 1) {
- mod_timer(&ap_dev->timeout, jiffies + timeout);
- ap_dev->reset = AP_RESET_ARMED;
+ 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);
+}
+
+/**
+ * ap_poll_thread(): Thread that polls for finished requests.
+ * @data: Unused pointer
+ *
+ * AP bus poll thread. The purpose of this thread is to poll for
+ * finished requests in a loop if there is a "free" cpu - that is
+ * a cpu that doesn't have anything better to do. The polling stops
+ * as soon as there is another task or if all messages have been
+ * delivered.
+ */
+static int ap_poll_thread(void *data)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ set_user_nice(current, MAX_NICE);
+ set_freezable();
+ while (!kthread_should_stop()) {
+ add_wait_queue(&ap_poll_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (ap_suspend_flag ||
+ atomic_read(&ap_poll_requests) <= 0) {
+ schedule();
+ try_to_freeze();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&ap_poll_wait, &wait);
+ if (need_resched()) {
+ schedule();
+ try_to_freeze();
+ continue;
+ }
+ ap_tasklet_fn(0);
+ } while (!kthread_should_stop());
+ return 0;
+}
+
+static int ap_poll_thread_start(void)
+{
+ int rc;
+
+ if (ap_using_interrupts() || ap_poll_kthread)
+ return 0;
+ mutex_lock(&ap_poll_thread_mutex);
+ ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
+ rc = PTR_RET(ap_poll_kthread);
+ if (rc)
+ ap_poll_kthread = NULL;
+ mutex_unlock(&ap_poll_thread_mutex);
+ return rc;
+}
+
+static void ap_poll_thread_stop(void)
+{
+ if (!ap_poll_kthread)
+ return;
+ mutex_lock(&ap_poll_thread_mutex);
+ kthread_stop(ap_poll_kthread);
+ ap_poll_kthread = NULL;
+ mutex_unlock(&ap_poll_thread_mutex);
+}
+
+/**
+ * ap_queue_message(): Queue a request to an AP device.
+ * @ap_dev: The AP device to queue the message to
+ * @ap_msg: The message that is to be added
+ */
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+ /* For asynchronous message handling a valid receive-callback
+ * is required. */
+ BUG_ON(!ap_msg->receive);
+
+ spin_lock_bh(&ap_dev->lock);
+ /* Queue the message. */
+ list_add_tail(&ap_msg->list, &ap_dev->requestq);
+ ap_dev->requestq_count++;
+ ap_dev->total_request_count++;
+ /* Send/receive as many request from the queue as possible. */
+ ap_sm_wait(ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
+ spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * ap_cancel_message(): Cancel a crypto request.
+ * @ap_dev: The AP device that has the message queued
+ * @ap_msg: The message that is to be removed
+ *
+ * Cancel a crypto request. This is done by removing the request
+ * from the device pending or request queue. Note that the
+ * request stays on the AP queue. When it finishes the message
+ * reply will be discarded because the psmid can't be found.
+ */
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+ struct ap_message *tmp;
+
+ spin_lock_bh(&ap_dev->lock);
+ if (!list_empty(&ap_msg->list)) {
+ list_for_each_entry(tmp, &ap_dev->pendingq, list)
+ if (tmp->psmid == ap_msg->psmid) {
+ ap_dev->pendingq_count--;
+ goto found;
+ }
+ ap_dev->requestq_count--;
+found:
+ list_del_init(&ap_msg->list);
+ }
+ spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
+/*
* AP device related attributes.
*/
static ssize_t ap_hwtype_show(struct device *dev,
@@ -690,21 +1099,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;
@@ -719,17 +1124,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;
}
@@ -823,99 +1223,95 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
return retval;
}
-static int ap_bus_suspend(struct device *dev, pm_message_t state)
+static int ap_dev_suspend(struct device *dev, pm_message_t state)
{
struct ap_device *ap_dev = to_ap_dev(dev);
- unsigned long flags;
-
- if (!ap_suspend_flag) {
- ap_suspend_flag = 1;
-
- /* Disable scanning for devices, thus we do not want to scan
- * for them after removing.
- */
- del_timer_sync(&ap_config_timer);
- if (ap_work_queue != NULL) {
- destroy_workqueue(ap_work_queue);
- ap_work_queue = NULL;
- }
- tasklet_disable(&ap_tasklet);
- }
/* Poll on the device until all requests are finished. */
- do {
- flags = 0;
- spin_lock_bh(&ap_dev->lock);
- __ap_poll_device(ap_dev, &flags);
- spin_unlock_bh(&ap_dev->lock);
- } while ((flags & 1) || (flags & 2));
-
spin_lock_bh(&ap_dev->lock);
- ap_dev->unregistered = 1;
+ ap_dev->state = AP_STATE_SUSPEND_WAIT;
+ while (ap_sm_event(ap_dev, AP_EVENT_POLL) != AP_WAIT_NONE)
+ ;
+ ap_dev->state = AP_STATE_BORKED;
spin_unlock_bh(&ap_dev->lock);
+ return 0;
+}
+static int ap_dev_resume(struct device *dev)
+{
return 0;
}
-static int ap_bus_resume(struct device *dev)
+static void ap_bus_suspend(void)
+{
+ ap_suspend_flag = 1;
+ /*
+ * Disable scanning for devices, thus we do not want to scan
+ * for them after removing.
+ */
+ flush_work(&ap_scan_work);
+ tasklet_disable(&ap_tasklet);
+}
+
+static int __ap_devices_unregister(struct device *dev, void *dummy)
+{
+ device_unregister(dev);
+ return 0;
+}
+
+static void ap_bus_resume(void)
{
- struct ap_device *ap_dev = to_ap_dev(dev);
int rc;
- if (ap_suspend_flag) {
- ap_suspend_flag = 0;
- if (ap_interrupts_available()) {
- if (!ap_using_interrupts()) {
- rc = register_adapter_interrupt(&ap_airq);
- ap_airq_flag = (rc == 0);
- }
- } else {
- if (ap_using_interrupts()) {
- unregister_adapter_interrupt(&ap_airq);
- ap_airq_flag = 0;
- }
- }
- ap_query_configuration();
- if (!user_set_domain) {
- ap_domain_index = -1;
- ap_select_domain();
- }
- init_timer(&ap_config_timer);
- ap_config_timer.function = ap_config_timeout;
- ap_config_timer.data = 0;
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
- ap_work_queue = create_singlethread_workqueue("kapwork");
- if (!ap_work_queue)
- return -ENOMEM;
- tasklet_enable(&ap_tasklet);
- if (!ap_using_interrupts())
- ap_schedule_poll_timer();
- else
- tasklet_schedule(&ap_tasklet);
- if (ap_thread_flag)
- rc = ap_poll_thread_start();
- else
- rc = 0;
- } else
- rc = 0;
- if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {
- spin_lock_bh(&ap_dev->lock);
- ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid),
- ap_domain_index);
- spin_unlock_bh(&ap_dev->lock);
+ /* Unconditionally remove all AP devices */
+ bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
+ /* Reset thin interrupt setting */
+ if (ap_interrupts_available() && !ap_using_interrupts()) {
+ rc = register_adapter_interrupt(&ap_airq);
+ ap_airq_flag = (rc == 0);
+ }
+ if (!ap_interrupts_available() && ap_using_interrupts()) {
+ unregister_adapter_interrupt(&ap_airq);
+ ap_airq_flag = 0;
}
- queue_work(ap_work_queue, &ap_config_work);
+ /* Reset domain */
+ if (!user_set_domain)
+ ap_domain_index = -1;
+ /* Get things going again */
+ ap_suspend_flag = 0;
+ if (ap_airq_flag)
+ xchg(ap_airq.lsi_ptr, 0);
+ tasklet_enable(&ap_tasklet);
+ queue_work(system_long_wq, &ap_scan_work);
+}
- return rc;
+static int ap_power_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ switch (event) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ ap_bus_suspend();
+ break;
+ case PM_POST_HIBERNATION:
+ case PM_POST_SUSPEND:
+ ap_bus_resume();
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
}
+static struct notifier_block ap_power_notifier = {
+ .notifier_call = ap_power_event,
+};
static struct bus_type ap_bus_type = {
.name = "ap",
.match = &ap_bus_match,
.uevent = &ap_uevent,
- .suspend = ap_bus_suspend,
- .resume = ap_bus_resume
+ .suspend = ap_dev_suspend,
+ .resume = ap_dev_resume,
};
static int ap_device_probe(struct device *dev)
@@ -925,21 +1321,9 @@ static int ap_device_probe(struct device *dev)
int rc;
ap_dev->drv = ap_drv;
-
- spin_lock_bh(&ap_device_list_lock);
- list_add(&ap_dev->list, &ap_device_list);
- spin_unlock_bh(&ap_device_list_lock);
-
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
- if (rc) {
- spin_lock_bh(&ap_device_list_lock);
- list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_device_list_lock);
- } 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;
}
@@ -956,12 +1340,14 @@ static void __ap_flush_queue(struct ap_device *ap_dev)
list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) {
list_del_init(&ap_msg->list);
ap_dev->pendingq_count--;
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ ap_msg->rc = -EAGAIN;
+ ap_msg->receive(ap_dev, ap_msg, NULL);
}
list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) {
list_del_init(&ap_msg->list);
ap_dev->requestq_count--;
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+ ap_msg->rc = -EAGAIN;
+ ap_msg->receive(ap_dev, ap_msg, NULL);
}
}
@@ -991,11 +1377,19 @@ static int ap_device_remove(struct device *dev)
return 0;
}
+static void ap_device_release(struct device *dev)
+{
+ kfree(to_ap_dev(dev));
+}
+
int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
char *name)
{
struct device_driver *drv = &ap_drv->driver;
+ if (!initialised)
+ return -ENODEV;
+
drv->bus = &ap_bus_type;
drv->probe = ap_device_probe;
drv->remove = ap_device_remove;
@@ -1013,60 +1407,16 @@ EXPORT_SYMBOL(ap_driver_unregister);
void ap_bus_force_rescan(void)
{
- /* reconfigure the AP bus rescan timer. */
- mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
+ if (ap_suspend_flag)
+ return;
/* processing a asynchronous bus rescan */
- queue_work(ap_work_queue, &ap_config_work);
- flush_work(&ap_config_work);
+ del_timer(&ap_config_timer);
+ queue_work(system_long_wq, &ap_scan_work);
+ flush_work(&ap_scan_work);
}
EXPORT_SYMBOL(ap_bus_force_rescan);
/*
- * ap_test_config(): helper function to extract the nrth bit
- * within the unsigned int array field.
- */
-static inline int ap_test_config(unsigned int *field, unsigned int nr)
-{
- if (nr > 0xFFu)
- return 0;
- return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
-}
-
-/*
- * ap_test_config_card_id(): Test, whether an AP card ID is configured.
- * @id AP card ID
- *
- * Returns 0 if the card is not configured
- * 1 if the card is configured or
- * if the configuration information is not available
- */
-static inline int ap_test_config_card_id(unsigned int id)
-{
- if (!ap_configuration)
- return 1;
- return ap_test_config(ap_configuration->apm, id);
-}
-
-/*
- * ap_test_config_domain(): Test, whether an AP usage domain is configured.
- * @domain AP usage domain ID
- *
- * Returns 0 if the usage domain is not configured
- * 1 if the usage domain is configured or
- * if the configuration information is not available
- */
-static inline int ap_test_config_domain(unsigned int domain)
-{
- if (!ap_configuration) /* QCI not supported */
- if (domain < 16)
- return 1; /* then domains 0...15 are configured */
- else
- return 0;
- else
- return ap_test_config(ap_configuration->aqm, domain);
-}
-
-/*
* AP bus attributes.
*/
static ssize_t ap_domain_show(struct bus_type *bus, char *buf)
@@ -1078,21 +1428,20 @@ static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);
static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
{
- if (ap_configuration != NULL) { /* QCI not supported */
- if (test_facility(76)) { /* format 1 - 256 bit domain field */
- return snprintf(buf, PAGE_SIZE,
- "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+ if (!ap_configuration) /* QCI not supported */
+ return snprintf(buf, PAGE_SIZE, "not supported\n");
+ if (!test_facility(76))
+ /* format 0 - 16 bit domain field */
+ return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
+ ap_configuration->adm[0],
+ ap_configuration->adm[1]);
+ /* format 1 - 256 bit domain field */
+ return snprintf(buf, PAGE_SIZE,
+ "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
ap_configuration->adm[0], ap_configuration->adm[1],
ap_configuration->adm[2], ap_configuration->adm[3],
ap_configuration->adm[4], ap_configuration->adm[5],
ap_configuration->adm[6], ap_configuration->adm[7]);
- } else { /* format 0 - 16 bit domain field */
- return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
- ap_configuration->adm[0], ap_configuration->adm[1]);
- }
- } else {
- return snprintf(buf, PAGE_SIZE, "not supported\n");
- }
}
static BUS_ATTR(ap_control_domain_mask, 0444,
@@ -1119,11 +1468,7 @@ static ssize_t ap_config_time_store(struct bus_type *bus,
if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
return -EINVAL;
ap_config_time = time;
- if (!timer_pending(&ap_config_timer) ||
- !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) {
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
- }
+ mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
return count;
}
@@ -1144,9 +1489,8 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus,
if (flag) {
rc = ap_poll_thread_start();
if (rc)
- return rc;
- }
- else
+ count = rc;
+ } else
ap_poll_thread_stop();
return count;
}
@@ -1184,35 +1528,12 @@ static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
{
- ap_qid_t qid;
- int i, nd, max_domain_id = -1;
- unsigned long fbits;
-
- if (ap_configuration) {
- if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) {
- for (i = 0; i < AP_DEVICES; i++) {
- if (!ap_test_config_card_id(i))
- continue;
- qid = AP_MKQID(i, ap_domain_index);
- fbits = ap_query_facilities(qid);
- if (fbits & (1UL << 57)) {
- /* the N bit is 0, Nd field is filled */
- nd = (int)((fbits & 0x00FF0000UL)>>16);
- if (nd > 0)
- max_domain_id = nd;
- else
- max_domain_id = 15;
- } else {
- /* N bit is 1, max 16 domains */
- max_domain_id = 15;
- }
- break;
- }
- }
- } else {
- /* no APXA support, older machines with max 16 domains */
+ int max_domain_id;
+
+ if (ap_configuration)
+ max_domain_id = ap_max_domain_id ? : -1;
+ else
max_domain_id = 15;
- }
return snprintf(buf, PAGE_SIZE, "%d\n", max_domain_id);
}
@@ -1230,44 +1551,22 @@ static struct bus_attribute *const ap_bus_attrs[] = {
};
/**
- * ap_query_configuration(): Query AP configuration information.
- *
- * Query information of installed cards and configured domains from AP.
- */
-static void ap_query_configuration(void)
-{
- if (ap_configuration_available()) {
- if (!ap_configuration)
- ap_configuration =
- kzalloc(sizeof(struct ap_config_info),
- GFP_KERNEL);
- if (ap_configuration)
- __ap_query_configuration(ap_configuration);
- } else
- ap_configuration = NULL;
-}
-
-/**
* ap_select_domain(): Select an AP domain.
*
* Pick one of the 16 AP domains.
*/
static int ap_select_domain(void)
{
- int queue_depth, device_type, count, max_count, best_domain;
- ap_qid_t qid;
- int rc, i, j;
-
- /* IF APXA isn't installed, only 16 domains could be defined */
- if (!ap_configuration->ap_extended && (ap_domain_index > 15))
- return -EINVAL;
+ int count, max_count, best_domain;
+ struct ap_queue_status status;
+ int i, j;
/*
* We want to use a single domain. Either the one specified with
* the "domain=" parameter or the domain with the maximum number
* of devices.
*/
- if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS)
+ if (ap_domain_index >= 0)
/* Domain has already been selected. */
return 0;
best_domain = -1;
@@ -1279,9 +1578,8 @@ static int ap_select_domain(void)
for (j = 0; j < AP_DEVICES; j++) {
if (!ap_test_config_card_id(j))
continue;
- qid = AP_MKQID(j, i);
- rc = ap_query_queue(qid, &queue_depth, &device_type);
- if (rc)
+ status = ap_test_queue(AP_MKQID(j, i), NULL);
+ if (status.response_code != AP_RESPONSE_NORMAL)
continue;
count++;
}
@@ -1298,109 +1596,6 @@ static int ap_select_domain(void)
}
/**
- * ap_probe_device_type(): Find the device type of an AP.
- * @ap_dev: pointer to the AP device.
- *
- * Find the device type if query queue returned a device type of 0.
- */
-static int ap_probe_device_type(struct ap_device *ap_dev)
-{
- static unsigned char msg[] = {
- 0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50,
- 0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,
- 0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00,
- 0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
- 0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20,
- 0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,
- 0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22,
- 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
- 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
- 0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
- 0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
- 0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,
- 0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
- 0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,
- 0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00,
- 0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,
- 0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01,
- 0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,
- 0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68,
- 0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,
- 0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0,
- 0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,
- 0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04,
- 0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,
- 0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d,
- };
- struct ap_queue_status status;
- unsigned long long psmid;
- char *reply;
- int rc, i;
-
- reply = (void *) get_zeroed_page(GFP_KERNEL);
- if (!reply) {
- rc = -ENOMEM;
- goto out;
- }
-
- status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
- msg, sizeof(msg), 0);
- if (status.response_code != AP_RESPONSE_NORMAL) {
- rc = -ENODEV;
- goto out_free;
- }
-
- /* Wait for the test message to complete. */
- for (i = 0; i < 6; i++) {
- 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;
-}
-
-static void ap_interrupt_handler(struct airq_struct *airq)
-{
- inc_irq_stat(IRQIO_APB);
- tasklet_schedule(&ap_tasklet);
-}
-
-/**
* __ap_scan_bus(): Scan the AP bus.
* @dev: Pointer to device
* @data: Pointer to data
@@ -1412,49 +1607,38 @@ 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) {
- return;
- }
+ if (ap_select_domain() != 0)
+ goto out;
+
for (i = 0; i < AP_DEVICES; i++) {
qid = AP_MKQID(i, ap_domain_index);
dev = bus_find_device(&ap_bus_type, NULL,
(void *)(unsigned long)qid,
__ap_scan_bus);
- if (ap_test_config_card_id(i))
- rc = ap_query_queue(qid, &queue_depth, &device_type);
- else
- rc = -ENODEV;
+ rc = ap_query_queue(qid, &queue_depth, &device_type,
+ &device_functions);
if (dev) {
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;
@@ -1462,525 +1646,72 @@ 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->unregistered = 1;
+ ap_dev->raw_hwtype = device_type;
+ ap_dev->device_type = device_type;
+ ap_dev->functions = device_functions;
spin_lock_init(&ap_dev->lock);
INIT_LIST_HEAD(&ap_dev->pendingq);
INIT_LIST_HEAD(&ap_dev->requestq);
INIT_LIST_HEAD(&ap_dev->list);
setup_timer(&ap_dev->timeout, ap_request_timeout,
(unsigned long) ap_dev);
- switch (device_type) {
- case 0:
- /* device type probing for old cards */
- if (ap_probe_device_type(ap_dev)) {
- kfree(ap_dev);
- continue;
- }
- break;
- default:
- ap_dev->device_type = device_type;
- }
- ap_dev->raw_hwtype = device_type;
-
- rc = ap_query_functions(qid, &device_functions);
- if (!rc)
- ap_dev->functions = device_functions;
- else
- ap_dev->functions = 0u;
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
- if (dev_set_name(&ap_dev->device, "card%02x",
- AP_QID_DEVICE(ap_dev->qid))) {
+ rc = dev_set_name(&ap_dev->device, "card%02x",
+ AP_QID_DEVICE(ap_dev->qid));
+ if (rc) {
kfree(ap_dev);
continue;
}
+ /* Add to list of devices */
+ spin_lock_bh(&ap_device_list_lock);
+ list_add(&ap_dev->list, &ap_device_list);
+ spin_unlock_bh(&ap_device_list_lock);
+ /* Start with a device reset */
+ spin_lock_bh(&ap_dev->lock);
+ ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_POLL));
+ spin_unlock_bh(&ap_dev->lock);
+ /* Register device */
ap_dev->device.release = ap_device_release;
rc = device_register(&ap_dev->device);
if (rc) {
+ spin_lock_bh(&ap_dev->lock);
+ list_del_init(&ap_dev->list);
+ spin_unlock_bh(&ap_dev->lock);
put_device(&ap_dev->device);
continue;
}
/* Add device attributes. */
rc = sysfs_create_group(&ap_dev->device.kobj,
&ap_dev_attr_group);
- if (!rc) {
- spin_lock_bh(&ap_dev->lock);
- ap_dev->unregistered = 0;
- spin_unlock_bh(&ap_dev->lock);
- }
- else
+ if (rc) {
device_unregister(&ap_dev->device);
- }
-}
-
-static void
-ap_config_timeout(unsigned long ptr)
-{
- queue_work(ap_work_queue, &ap_config_work);
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
-}
-
-/**
- * ap_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)
-{
- int rc, depth, type;
- struct ap_queue_status status;
-
-
- if (ap_dev->reset == AP_RESET_IN_PROGRESS) {
- status = ap_test_queue(ap_dev->qid, &depth, &type);
- 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, &depth, &type);
- 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->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
- return -EINVAL;
- default: /* Device is gone. */
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
- return -ENODEV;
- }
- } else {
- list_add_tail(&ap_msg->list, &ap_dev->requestq);
- ap_dev->requestq_count++;
- ap_dev->total_request_count++;
- return -EBUSY;
- }
- ap_schedule_poll_timer();
- return 0;
-}
-
-void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
- unsigned long flags;
- int rc;
-
- /* For asynchronous message handling a valid receive-callback
- * is required. */
- BUG_ON(!ap_msg->receive);
-
- spin_lock_bh(&ap_dev->lock);
- if (!ap_dev->unregistered) {
- /* Make room on the queue by polling for finished requests. */
- rc = ap_poll_queue(ap_dev, &flags);
- if (!rc)
- rc = __ap_queue_message(ap_dev, ap_msg);
- if (!rc)
- wake_up(&ap_poll_wait);
- if (rc == -ENODEV)
- ap_dev->unregistered = 1;
- } else {
- ap_msg->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
- rc = -ENODEV;
- }
- spin_unlock_bh(&ap_dev->lock);
- if (rc == -ENODEV)
- device_unregister(&ap_dev->device);
-}
-EXPORT_SYMBOL(ap_queue_message);
-
-/**
- * ap_cancel_message(): Cancel a crypto request.
- * @ap_dev: The AP device that has the message queued
- * @ap_msg: The message that is to be removed
- *
- * Cancel a crypto request. This is done by removing the request
- * from the device pending or request queue. Note that the
- * request stays on the AP queue. When it finishes the message
- * reply will be discarded because the psmid can't be found.
- */
-void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
- struct ap_message *tmp;
-
- spin_lock_bh(&ap_dev->lock);
- if (!list_empty(&ap_msg->list)) {
- list_for_each_entry(tmp, &ap_dev->pendingq, list)
- if (tmp->psmid == ap_msg->psmid) {
- ap_dev->pendingq_count--;
- goto found;
- }
- ap_dev->requestq_count--;
- found:
- list_del_init(&ap_msg->list);
- }
- spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_cancel_message);
-
-/**
- * ap_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused pointer.
- *
- * Schedules the AP tasklet using a high resolution timer.
- */
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
-{
- tasklet_schedule(&ap_tasklet);
- return HRTIMER_NORESTART;
-}
-
-/**
- * ap_reset(): Reset a not responding AP device.
- * @ap_dev: Pointer to the AP device
- *
- * Reset a not responding AP device and move all requests from the
- * pending queue to the request queue.
- */
-static void ap_reset(struct ap_device *ap_dev, 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_poll_thread(): Thread that polls for finished requests.
- * @data: Unused pointer
- *
- * AP bus poll thread. The purpose of this thread is to poll for
- * finished requests in a loop if there is a "free" cpu - that is
- * a cpu that doesn't have anything better to do. The polling stops
- * as soon as there is another task or if all messages have been
- * delivered.
- */
-static int ap_poll_thread(void *data)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- int requests;
- struct ap_device *ap_dev;
-
- set_user_nice(current, MAX_NICE);
- while (1) {
- if (ap_suspend_flag)
- return 0;
- if (need_resched()) {
- schedule();
continue;
}
- add_wait_queue(&ap_poll_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop())
- break;
- requests = atomic_read(&ap_poll_requests);
- if (requests <= 0)
- schedule();
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&ap_poll_wait, &wait);
-
- flags = 0;
- spin_lock_bh(&ap_device_list_lock);
- list_for_each_entry(ap_dev, &ap_device_list, list) {
- spin_lock(&ap_dev->lock);
- __ap_poll_device(ap_dev, &flags);
- spin_unlock(&ap_dev->lock);
- }
- spin_unlock_bh(&ap_device_list_lock);
}
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&ap_poll_wait, &wait);
- return 0;
-}
-
-static int ap_poll_thread_start(void)
-{
- int rc;
-
- if (ap_using_interrupts() || ap_suspend_flag)
- return 0;
- mutex_lock(&ap_poll_thread_mutex);
- if (!ap_poll_kthread) {
- ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
- rc = PTR_RET(ap_poll_kthread);
- if (rc)
- ap_poll_kthread = NULL;
- }
- else
- rc = 0;
- mutex_unlock(&ap_poll_thread_mutex);
- return rc;
-}
-
-static void ap_poll_thread_stop(void)
-{
- mutex_lock(&ap_poll_thread_mutex);
- if (ap_poll_kthread) {
- kthread_stop(ap_poll_kthread);
- ap_poll_kthread = NULL;
- }
- mutex_unlock(&ap_poll_thread_mutex);
+out:
+ mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
}
-/**
- * ap_request_timeout(): Handling of request timeouts
- * @data: Holds the AP device.
- *
- * Handles request timeouts.
- */
-static void ap_request_timeout(unsigned long data)
+static void ap_config_timeout(unsigned long ptr)
{
- struct ap_device *ap_dev = (struct ap_device *) data;
-
- if (ap_dev->reset == AP_RESET_ARMED) {
- ap_dev->reset = AP_RESET_DO;
-
- if (ap_using_interrupts())
- tasklet_schedule(&ap_tasklet);
- }
+ if (ap_suspend_flag)
+ return;
+ queue_work(system_long_wq, &ap_scan_work);
}
static void ap_reset_domain(void)
{
int i;
- if ((ap_domain_index != -1) && (ap_test_config_domain(ap_domain_index)))
- for (i = 0; i < AP_DEVICES; i++)
- ap_reset_queue(AP_MKQID(i, ap_domain_index));
+ if (ap_domain_index == -1 || !ap_test_config_domain(ap_domain_index))
+ return;
+ for (i = 0; i < AP_DEVICES; i++)
+ ap_reset_queue(AP_MKQID(i, ap_domain_index));
}
static void ap_reset_all(void)
@@ -2009,11 +1740,24 @@ static struct reset_call ap_reset_call = {
*/
int __init ap_module_init(void)
{
+ int max_domain_id;
int rc, i;
- if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
- pr_warning("%d is not a valid cryptographic domain\n",
- ap_domain_index);
+ if (ap_instructions_available() != 0) {
+ pr_warn("The hardware system does not support AP instructions\n");
+ return -ENODEV;
+ }
+
+ /* Get AP configuration data if available */
+ ap_init_configuration();
+
+ if (ap_configuration)
+ max_domain_id = ap_max_domain_id ? : (AP_DOMAINS - 1);
+ else
+ max_domain_id = 15;
+ if (ap_domain_index < -1 || ap_domain_index > max_domain_id) {
+ pr_warn("%d is not a valid cryptographic domain\n",
+ ap_domain_index);
return -EINVAL;
}
/* In resume callback we need to know if the user had set the domain.
@@ -2022,11 +1766,6 @@ int __init ap_module_init(void)
if (ap_domain_index >= 0)
user_set_domain = 1;
- if (ap_instructions_available() != 0) {
- pr_warning("The hardware system does not support "
- "AP instructions\n");
- return -ENODEV;
- }
if (ap_interrupts_available()) {
rc = register_adapter_interrupt(&ap_airq);
ap_airq_flag = (rc == 0);
@@ -2050,24 +1789,11 @@ int __init ap_module_init(void)
if (rc)
goto out_bus;
- ap_work_queue = create_singlethread_workqueue("kapwork");
- if (!ap_work_queue) {
- rc = -ENOMEM;
- goto out_root;
- }
-
- ap_query_configuration();
- if (ap_select_domain() == 0)
- ap_scan_bus(NULL);
-
/* Setup the AP bus rescan timer. */
- init_timer(&ap_config_timer);
- ap_config_timer.function = ap_config_timeout;
- ap_config_timer.data = 0;
- ap_config_timer.expires = jiffies + ap_config_time * HZ;
- add_timer(&ap_config_timer);
+ setup_timer(&ap_config_timer, ap_config_timeout, 0);
- /* Setup the high resultion poll timer.
+ /*
+ * Setup the high resultion poll timer.
* If we are running under z/VM adjust polling to z/VM polling rate.
*/
if (MACHINE_IS_VM)
@@ -2083,13 +1809,19 @@ int __init ap_module_init(void)
goto out_work;
}
+ rc = register_pm_notifier(&ap_power_notifier);
+ if (rc)
+ goto out_pm;
+
+ queue_work(system_long_wq, &ap_scan_work);
+ initialised = true;
+
return 0;
+out_pm:
+ ap_poll_thread_stop();
out_work:
- del_timer_sync(&ap_config_timer);
hrtimer_cancel(&ap_poll_timer);
- destroy_workqueue(ap_work_queue);
-out_root:
root_device_unregister(ap_root_device);
out_bus:
while (i--)
@@ -2099,14 +1831,10 @@ out:
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
+ kfree(ap_configuration);
return rc;
}
-static int __ap_match_all(struct device *dev, void *data)
-{
- return 1;
-}
-
/**
* ap_modules_exit(): The module termination code
*
@@ -2115,24 +1843,20 @@ static int __ap_match_all(struct device *dev, void *data)
void ap_module_exit(void)
{
int i;
- struct device *dev;
+ initialised = false;
ap_reset_domain();
ap_poll_thread_stop();
del_timer_sync(&ap_config_timer);
hrtimer_cancel(&ap_poll_timer);
- destroy_workqueue(ap_work_queue);
tasklet_kill(&ap_tasklet);
- while ((dev = bus_find_device(&ap_bus_type, NULL, NULL,
- __ap_match_all)))
- {
- device_unregister(dev);
- put_device(dev);
- }
+ bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
for (i = 0; ap_bus_attrs[i]; i++)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+ unregister_pm_notifier(&ap_power_notifier);
root_device_unregister(ap_root_device);
bus_unregister(&ap_bus_type);
+ kfree(ap_configuration);
unregister_reset_call(&ap_reset_call);
if (ap_using_interrupts())
unregister_adapter_interrupt(&ap_airq);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 00468c8d0..6adcbdf22 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;
/**
@@ -75,21 +72,9 @@ struct ap_queue_status {
unsigned int pad2 : 16;
} __packed;
-#define AP_QUEUE_STATUS_INVALID \
- { 1, 1, 1, 0xF, 1, 0xFF, 0xFFFF }
-
-static inline
-int ap_queue_status_invalid_test(struct ap_queue_status *status)
-{
- struct ap_queue_status invalid = AP_QUEUE_STATUS_INVALID;
- return !(memcmp(status, &invalid, sizeof(struct ap_queue_status)));
-}
-#define AP_MAX_BITS 31
static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
{
- if (nr > AP_MAX_BITS)
- return 0;
return (*ptr & (0x80000000u >> nr)) != 0;
}
@@ -132,19 +117,45 @@ static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
#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;
@@ -163,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. */
@@ -199,6 +212,7 @@ struct ap_message {
unsigned long long psmid; /* Message id. */
void *message; /* Pointer to message buffer. */
size_t length; /* Message length. */
+ int rc; /* Return code for this message */
void *private; /* ap driver private pointer. */
unsigned int special:1; /* Used for special commands. */
@@ -231,6 +245,7 @@ static inline void ap_init_message(struct ap_message *ap_msg)
{
ap_msg->psmid = 0;
ap_msg->length = 0;
+ ap_msg->rc = 0;
ap_msg->special = 0;
ap_msg->receive = NULL;
}
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 4eb45546a..9f8fa42c0 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -317,11 +317,9 @@ EXPORT_SYMBOL(zcrypt_device_unregister);
void zcrypt_msgtype_register(struct zcrypt_ops *zops)
{
- if (zops->owner) {
- spin_lock_bh(&zcrypt_ops_list_lock);
- list_add_tail(&zops->list, &zcrypt_ops_list);
- spin_unlock_bh(&zcrypt_ops_list_lock);
- }
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_add_tail(&zops->list, &zcrypt_ops_list);
+ spin_unlock_bh(&zcrypt_ops_list_lock);
}
EXPORT_SYMBOL(zcrypt_msgtype_register);
@@ -342,7 +340,7 @@ struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
spin_lock_bh(&zcrypt_ops_list_lock);
list_for_each_entry(zops, &zcrypt_ops_list, list) {
if ((zops->variant == variant) &&
- (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) {
+ (!strncmp(zops->name, name, sizeof(zops->name)))) {
found = 1;
break;
}
@@ -472,8 +470,7 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt)
unsigned long long z1, z2, z3;
int rc, copied;
- if (crt->outputdatalength < crt->inputdatalength ||
- (crt->inputdatalength & 1))
+ if (crt->outputdatalength < crt->inputdatalength)
return -EINVAL;
/*
* As long as outputdatalength is big enough, we can set the
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 750876891..38618f05a 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -96,6 +96,7 @@ struct zcrypt_ops {
struct list_head list; /* zcrypt ops list. */
struct module *owner;
int variant;
+ char name[128];
};
struct zcrypt_device {
diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h
index 1f42f103c..ca0cdbe46 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 334e282f2..74edf2934 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
@@ -395,10 +395,8 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
+ if (!reply)
+ goto out; /* ap_msg->rc indicates the error */
t80h = reply->message;
if (t80h->type == TYPE80_RSP_CODE) {
if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
@@ -449,10 +447,12 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -493,10 +493,12 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
init_completion(&work);
ap_queue_message(zdev->ap_dev, &ap_msg);
rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
+ if (rc == 0) {
+ rc = ap_msg.rc;
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ } else
/* Signal pending. */
ap_cancel_message(zdev->ap_dev, &ap_msg);
out_free:
@@ -511,6 +513,7 @@ static struct zcrypt_ops zcrypt_msgtype50_ops = {
.rsa_modexpo = zcrypt_cex2a_modexpo,
.rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
.owner = THIS_MODULE,
+ .name = MSGTYPE50_NAME,
.variant = MSGTYPE50_VARIANT_DEFAULT,
};
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 46b324ce6..9a2dd472c 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);
@@ -1110,6 +1119,7 @@ static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
*/
static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_NORNG,
.rsa_modexpo = zcrypt_msgtype6_modexpo,
.rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
@@ -1118,6 +1128,7 @@ static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
static struct zcrypt_ops zcrypt_msgtype6_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_DEFAULT,
.rsa_modexpo = zcrypt_msgtype6_modexpo,
.rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
@@ -1127,6 +1138,7 @@ static struct zcrypt_ops zcrypt_msgtype6_ops = {
static struct zcrypt_ops zcrypt_msgtype6_ep11_ops = {
.owner = THIS_MODULE,
+ .name = MSGTYPE06_NAME,
.variant = MSGTYPE06_VARIANT_EP11,
.rsa_modexpo = NULL,
.rsa_modexpo_crt = NULL,
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
deleted file mode 100644
index 7a743f4c6..000000000
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- * Ralph Wuerthner <rwuerthn@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define KMSG_COMPONENT "zcrypt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/atomic.h>
-#include <asm/uaccess.h>
-
-#include "ap_bus.h"
-#include "zcrypt_api.h"
-#include "zcrypt_error.h"
-#include "zcrypt_pcica.h"
-
-#define PCICA_MIN_MOD_SIZE 1 /* 8 bits */
-#define PCICA_MAX_MOD_SIZE 256 /* 2048 bits */
-
-#define PCICA_SPEED_RATING 2800
-
-#define PCICA_MAX_MESSAGE_SIZE 0x3a0 /* sizeof(struct type4_lcr) */
-#define PCICA_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
-
-#define PCICA_CLEANUP_TIME (15*HZ)
-
-static struct ap_device_id zcrypt_pcica_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_PCICA) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_pcica_ids);
-MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCICA Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
-MODULE_LICENSE("GPL");
-
-static int zcrypt_pcica_probe(struct ap_device *ap_dev);
-static void zcrypt_pcica_remove(struct ap_device *ap_dev);
-static void zcrypt_pcica_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
-static struct ap_driver zcrypt_pcica_driver = {
- .probe = zcrypt_pcica_probe,
- .remove = zcrypt_pcica_remove,
- .ids = zcrypt_pcica_ids,
- .request_timeout = PCICA_CLEANUP_TIME,
-};
-
-/**
- * Convert a ICAMEX message to a type4 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type4MEX_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- unsigned char *modulus, *exponent, *message;
- int mod_len;
-
- mod_len = mex->inputdatalength;
-
- if (mod_len <= 128) {
- struct type4_sme *sme = ap_msg->message;
- memset(sme, 0, sizeof(*sme));
- ap_msg->length = sizeof(*sme);
- sme->header.msg_fmt = TYPE4_SME_FMT;
- sme->header.msg_len = sizeof(*sme);
- sme->header.msg_type_code = TYPE4_TYPE_CODE;
- sme->header.request_code = TYPE4_REQU_CODE;
- modulus = sme->modulus + sizeof(sme->modulus) - mod_len;
- exponent = sme->exponent + sizeof(sme->exponent) - mod_len;
- message = sme->message + sizeof(sme->message) - mod_len;
- } else {
- struct type4_lme *lme = ap_msg->message;
- memset(lme, 0, sizeof(*lme));
- ap_msg->length = sizeof(*lme);
- lme->header.msg_fmt = TYPE4_LME_FMT;
- lme->header.msg_len = sizeof(*lme);
- lme->header.msg_type_code = TYPE4_TYPE_CODE;
- lme->header.request_code = TYPE4_REQU_CODE;
- modulus = lme->modulus + sizeof(lme->modulus) - mod_len;
- exponent = lme->exponent + sizeof(lme->exponent) - mod_len;
- message = lme->message + sizeof(lme->message) - mod_len;
- }
-
- if (copy_from_user(modulus, mex->n_modulus, mod_len) ||
- copy_from_user(exponent, mex->b_key, mod_len) ||
- copy_from_user(message, mex->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type4 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type4CRT_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- unsigned char *p, *q, *dp, *dq, *u, *inp;
- int mod_len, short_len, long_len;
-
- mod_len = crt->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
-
- if (mod_len <= 128) {
- struct type4_scr *scr = ap_msg->message;
- memset(scr, 0, sizeof(*scr));
- ap_msg->length = sizeof(*scr);
- scr->header.msg_type_code = TYPE4_TYPE_CODE;
- scr->header.request_code = TYPE4_REQU_CODE;
- scr->header.msg_fmt = TYPE4_SCR_FMT;
- scr->header.msg_len = sizeof(*scr);
- p = scr->p + sizeof(scr->p) - long_len;
- q = scr->q + sizeof(scr->q) - short_len;
- dp = scr->dp + sizeof(scr->dp) - long_len;
- dq = scr->dq + sizeof(scr->dq) - short_len;
- u = scr->u + sizeof(scr->u) - long_len;
- inp = scr->message + sizeof(scr->message) - mod_len;
- } else {
- struct type4_lcr *lcr = ap_msg->message;
- memset(lcr, 0, sizeof(*lcr));
- ap_msg->length = sizeof(*lcr);
- lcr->header.msg_type_code = TYPE4_TYPE_CODE;
- lcr->header.request_code = TYPE4_REQU_CODE;
- lcr->header.msg_fmt = TYPE4_LCR_FMT;
- lcr->header.msg_len = sizeof(*lcr);
- p = lcr->p + sizeof(lcr->p) - long_len;
- q = lcr->q + sizeof(lcr->q) - short_len;
- dp = lcr->dp + sizeof(lcr->dp) - long_len;
- dq = lcr->dq + sizeof(lcr->dq) - short_len;
- u = lcr->u + sizeof(lcr->u) - long_len;
- inp = lcr->message + sizeof(lcr->message) - mod_len;
- }
-
- if (copy_from_user(p, crt->np_prime, long_len) ||
- copy_from_user(q, crt->nq_prime, short_len) ||
- copy_from_user(dp, crt->bp_key, long_len) ||
- copy_from_user(dq, crt->bq_key, short_len) ||
- copy_from_user(u, crt->u_mult_inv, long_len) ||
- copy_from_user(inp, crt->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Copy results from a type 84 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int convert_type84(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type84_hdr *t84h = reply->message;
- char *data;
-
- if (t84h->len < sizeof(*t84h) + outputdatalength) {
- /* The result is too short, the PCICA card may not do that.. */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
- zdev->ap_dev->qid, zdev->online, t84h->code);
- return -EAGAIN; /* repeat the request on a different device. */
- }
- BUG_ON(t84h->len > PCICA_MAX_RESPONSE_SIZE);
- data = reply->message + t84h->len - outputdatalength;
- if (copy_to_user(outputdata, data, outputdatalength))
- return -EFAULT;
- return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE84_RSP_CODE:
- return convert_type84(zdev, reply,
- outputdata, outputdatalength);
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- pr_err("Cryptographic device %x failed and was set offline\n",
- zdev->ap_dev->qid);
- ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%dfail",
- zdev->ap_dev->qid, zdev->online);
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcica_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct type84_hdr *t84h;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t84h = reply->message;
- if (t84h->code == TYPE84_RSP_CODE) {
- length = min(PCICA_MAX_RESPONSE_SIZE, (int) t84h->len);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCICA
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICA device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcica_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type4MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCICA
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICA device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcica_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type4CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCICA card.
- */
-static struct zcrypt_ops zcrypt_pcica_ops = {
- .rsa_modexpo = zcrypt_pcica_modexpo,
- .rsa_modexpo_crt = zcrypt_pcica_modexpo_crt,
-};
-
-/**
- * Probe function for PCICA cards. It always accepts the AP device
- * since the bus_match already checked the hardware type.
- * @ap_dev: pointer to the AP device.
- */
-static int zcrypt_pcica_probe(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev;
- int rc;
-
- zdev = zcrypt_device_alloc(PCICA_MAX_RESPONSE_SIZE);
- if (!zdev)
- return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_pcica_ops;
- zdev->online = 1;
- zdev->user_space_type = ZCRYPT_PCICA;
- zdev->type_string = "PCICA";
- zdev->min_mod_size = PCICA_MIN_MOD_SIZE;
- zdev->max_mod_size = PCICA_MAX_MOD_SIZE;
- zdev->speed_rating = PCICA_SPEED_RATING;
- zdev->max_exp_bit_length = PCICA_MAX_MOD_SIZE;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- if (rc)
- goto out_free;
- return 0;
-
-out_free:
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
- return rc;
-}
-
-/**
- * This is called to remove the extended PCICA driver information
- * if an AP device is removed.
- */
-static void zcrypt_pcica_remove(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev = ap_dev->private;
-
- zcrypt_device_unregister(zdev);
-}
-
-int __init zcrypt_pcica_init(void)
-{
- return ap_driver_register(&zcrypt_pcica_driver, THIS_MODULE, "pcica");
-}
-
-void zcrypt_pcica_exit(void)
-{
- ap_driver_unregister(&zcrypt_pcica_driver);
-}
-
-module_init(zcrypt_pcica_init);
-module_exit(zcrypt_pcica_exit);
diff --git a/drivers/s390/crypto/zcrypt_pcica.h b/drivers/s390/crypto/zcrypt_pcica.h
deleted file mode 100644
index 9a59155ca..000000000
--- 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 <schwidefsky@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _ZCRYPT_PCICA_H_
-#define _ZCRYPT_PCICA_H_
-
-/**
- * The type 4 message family is associated with a PCICA card.
- *
- * The four members of the family are described below.
- *
- * Note that all unsigned char arrays are right-justified and left-padded
- * with zeroes.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type4_hdr {
- unsigned char reserved1;
- unsigned char msg_type_code; /* 0x04 */
- unsigned short msg_len;
- unsigned char request_code; /* 0x40 */
- unsigned char msg_fmt;
- unsigned short reserved2;
-} __attribute__((packed));
-
-#define TYPE4_TYPE_CODE 0x04
-#define TYPE4_REQU_CODE 0x40
-
-#define TYPE4_SME_FMT 0x00
-#define TYPE4_LME_FMT 0x10
-#define TYPE4_SCR_FMT 0x40
-#define TYPE4_LCR_FMT 0x50
-
-/* Mod-Exp, with a small modulus */
-struct type4_sme {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char exponent[128];
- unsigned char modulus[128];
-} __attribute__((packed));
-
-/* Mod-Exp, with a large modulus */
-struct type4_lme {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char exponent[256];
- unsigned char modulus[256];
-} __attribute__((packed));
-
-/* CRT, with a small modulus */
-struct type4_scr {
- struct type4_hdr header;
- unsigned char message[128];
- unsigned char dp[72];
- unsigned char dq[64];
- unsigned char p[72];
- unsigned char q[64];
- unsigned char u[72];
-} __attribute__((packed));
-
-/* CRT, with a large modulus */
-struct type4_lcr {
- struct type4_hdr header;
- unsigned char message[256];
- unsigned char dp[136];
- unsigned char dq[128];
- unsigned char p[136];
- unsigned char q[128];
- unsigned char u[136];
-} __attribute__((packed));
-
-/**
- * The type 84 response family is associated with a PCICA card.
- *
- * Note that all unsigned char arrays are right-justified and left-padded
- * with zeroes.
- *
- * Note that all reserved fields must be zeroes.
- */
-
-struct type84_hdr {
- unsigned char reserved1;
- unsigned char code;
- unsigned short len;
- unsigned char reserved2[4];
-} __attribute__((packed));
-
-#define TYPE84_RSP_CODE 0x84
-
-int zcrypt_pcica_init(void);
-void zcrypt_pcica_exit(void);
-
-#endif /* _ZCRYPT_PCICA_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
deleted file mode 100644
index 9f18876f0..000000000
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * zcrypt 2.1.0
- *
- * Copyright IBM Corp. 2001, 2006
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- * Ralph Wuerthner <rwuerthn@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define KMSG_COMPONENT "zcrypt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/gfp.h>
-#include <linux/err.h>
-#include <linux/atomic.h>
-#include <asm/uaccess.h>
-
-#include "ap_bus.h"
-#include "zcrypt_api.h"
-#include "zcrypt_error.h"
-#include "zcrypt_pcicc.h"
-#include "zcrypt_cca_key.h"
-
-#define PCICC_MIN_MOD_SIZE 64 /* 512 bits */
-#define PCICC_MAX_MOD_SIZE_OLD 128 /* 1024 bits */
-#define PCICC_MAX_MOD_SIZE 256 /* 2048 bits */
-
-/*
- * PCICC cards need a speed rating of 0. This keeps them at the end of
- * the zcrypt device list (see zcrypt_api.c). PCICC cards are only
- * used if no other cards are present because they are slow and can only
- * cope with PKCS12 padded requests. The logic is queer. PKCS11 padded
- * requests are rejected. The modexpo function encrypts PKCS12 padded data
- * and decrypts any non-PKCS12 padded data (except PKCS11) in the assumption
- * that it's encrypted PKCS12 data. The modexpo_crt function always decrypts
- * the data in the assumption that its PKCS12 encrypted data.
- */
-#define PCICC_SPEED_RATING 0
-
-#define PCICC_MAX_MESSAGE_SIZE 0x710 /* max size type6 v1 crt message */
-#define PCICC_MAX_RESPONSE_SIZE 0x710 /* max size type86 v1 reply */
-
-#define PCICC_CLEANUP_TIME (15*HZ)
-
-static struct ap_device_id zcrypt_pcicc_ids[] = {
- { AP_DEVICE(AP_DEVICE_TYPE_PCICC) },
- { /* end of list */ },
-};
-
-MODULE_DEVICE_TABLE(ap, zcrypt_pcicc_ids);
-MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCICC Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
-MODULE_LICENSE("GPL");
-
-static int zcrypt_pcicc_probe(struct ap_device *ap_dev);
-static void zcrypt_pcicc_remove(struct ap_device *ap_dev);
-static void zcrypt_pcicc_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
-
-static struct ap_driver zcrypt_pcicc_driver = {
- .probe = zcrypt_pcicc_probe,
- .remove = zcrypt_pcicc_remove,
- .ids = zcrypt_pcicc_ids,
- .request_timeout = PCICC_CLEANUP_TIME,
-};
-
-/**
- * The following is used to initialize the CPRB passed to the PCICC card
- * in a type6 message. The 3 fields that must be filled in at execution
- * time are req_parml, rpl_parml and usage_domain. Note that all three
- * fields are *little*-endian. Actually, everything about this interface
- * is ascii/little-endian, since the device has 'Intel inside'.
- *
- * The CPRB is followed immediately by the parm block.
- * The parm block contains:
- * - function code ('PD' 0x5044 or 'PK' 0x504B)
- * - rule block (0x0A00 'PKCS-1.2' or 0x0A00 'ZERO-PAD')
- * - VUD block
- */
-static struct CPRB static_cprb = {
- .cprb_len = 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 (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t86r = reply->message;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprb.cprb_ver_id == 0x01) {
- length = sizeof(struct type86_reply) + t86r->length - 2;
- length = min(PCICC_MAX_RESPONSE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCICC
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICC device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcicc_receive;
- ap_msg.length = PAGE_SIZE;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type6MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCICC
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCICC device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcicc_receive;
- ap_msg.length = PAGE_SIZE;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type6CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCICC card.
- */
-static struct zcrypt_ops zcrypt_pcicc_ops = {
- .rsa_modexpo = zcrypt_pcicc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcicc_modexpo_crt,
-};
-
-/**
- * Probe function for PCICC cards. It always accepts the AP device
- * since the bus_match already checked the hardware type.
- * @ap_dev: pointer to the AP device.
- */
-static int zcrypt_pcicc_probe(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev;
- int rc;
-
- zdev = zcrypt_device_alloc(PCICC_MAX_RESPONSE_SIZE);
- if (!zdev)
- return -ENOMEM;
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_pcicc_ops;
- zdev->online = 1;
- zdev->user_space_type = ZCRYPT_PCICC;
- zdev->type_string = "PCICC";
- zdev->min_mod_size = PCICC_MIN_MOD_SIZE;
- zdev->max_mod_size = PCICC_MAX_MOD_SIZE;
- zdev->speed_rating = PCICC_SPEED_RATING;
- zdev->max_exp_bit_length = PCICC_MAX_MOD_SIZE;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- if (rc)
- goto out_free;
- return 0;
-
- out_free:
- ap_dev->private = NULL;
- zcrypt_device_free(zdev);
- return rc;
-}
-
-/**
- * This is called to remove the extended PCICC driver information
- * if an AP device is removed.
- */
-static void zcrypt_pcicc_remove(struct ap_device *ap_dev)
-{
- struct zcrypt_device *zdev = ap_dev->private;
-
- zcrypt_device_unregister(zdev);
-}
-
-int __init zcrypt_pcicc_init(void)
-{
- return ap_driver_register(&zcrypt_pcicc_driver, THIS_MODULE, "pcicc");
-}
-
-void zcrypt_pcicc_exit(void)
-{
- ap_driver_unregister(&zcrypt_pcicc_driver);
-}
-
-module_init(zcrypt_pcicc_init);
-module_exit(zcrypt_pcicc_exit);
diff --git a/drivers/s390/crypto/zcrypt_pcicc.h b/drivers/s390/crypto/zcrypt_pcicc.h
deleted file mode 100644
index 7fe27e150..000000000
--- 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 <schwidefsky@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _ZCRYPT_PCICC_H_
-#define _ZCRYPT_PCICC_H_
-
-/**
- * The type 6 message family is associated with PCICC or PCIXCC cards.
- *
- * It contains a message header followed by a CPRB, both of which
- * are described below.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type6_hdr {
- unsigned char reserved1; /* 0x00 */
- unsigned char type; /* 0x06 */
- unsigned char reserved2[2]; /* 0x0000 */
- unsigned char right[4]; /* 0x00000000 */
- unsigned char reserved3[2]; /* 0x0000 */
- unsigned char reserved4[2]; /* 0x0000 */
- unsigned char apfs[4]; /* 0x00000000 */
- unsigned int offset1; /* 0x00000058 (offset to CPRB) */
- unsigned int offset2; /* 0x00000000 */
- unsigned int offset3; /* 0x00000000 */
- unsigned int offset4; /* 0x00000000 */
- unsigned char agent_id[16]; /* PCICC: */
- /* 0x0100 */
- /* 0x4343412d4150504c202020 */
- /* 0x010101 */
- /* PCIXCC: */
- /* 0x4341000000000000 */
- /* 0x0000000000000000 */
- unsigned char rqid[2]; /* rqid. internal to 603 */
- unsigned char reserved5[2]; /* 0x0000 */
- unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
- unsigned char reserved6[2]; /* 0x0000 */
- unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */
- unsigned int ToCardLen2; /* db len 0x00000000 for PKD */
- unsigned int ToCardLen3; /* 0x00000000 */
- unsigned int ToCardLen4; /* 0x00000000 */
- unsigned int FromCardLen1; /* response buffer length */
- unsigned int FromCardLen2; /* db len 0x00000000 for PKD */
- unsigned int FromCardLen3; /* 0x00000000 */
- unsigned int FromCardLen4; /* 0x00000000 */
-} __attribute__((packed));
-
-/**
- * CPRB
- * Note that all shorts, ints and longs are little-endian.
- * All pointer fields are 32-bits long, and mean nothing
- *
- * A request CPRB is followed by a request_parameter_block.
- *
- * The request (or reply) parameter block is organized thus:
- * function code
- * VUD block
- * key block
- */
-struct CPRB {
- unsigned short cprb_len; /* CPRB length */
- unsigned char cprb_ver_id; /* CPRB version id. */
- unsigned char pad_000; /* Alignment pad byte. */
- unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */
- unsigned char srpi_verb; /* SRPI verb type */
- unsigned char flags; /* flags */
- unsigned char func_id[2]; /* function id */
- unsigned char checkpoint_flag; /* */
- unsigned char resv2; /* reserved */
- unsigned short req_parml; /* request parameter buffer */
- /* length 16-bit little endian */
- unsigned char req_parmp[4]; /* request parameter buffer *
- * pointer (means nothing: the *
- * parameter buffer follows *
- * the CPRB). */
- unsigned char req_datal[4]; /* request data buffer */
- /* length ULELONG */
- unsigned char req_datap[4]; /* request data buffer */
- /* pointer */
- unsigned short rpl_parml; /* reply parameter buffer */
- /* length 16-bit little endian */
- unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */
- unsigned char rpl_parmp[4]; /* reply parameter buffer *
- * pointer (means nothing: the *
- * parameter buffer follows *
- * the CPRB). */
- unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */
- unsigned char rpl_datap[4]; /* reply data buffer */
- /* pointer */
- unsigned short ccp_rscode; /* server reason code ULESHORT */
- unsigned short ccp_rtcode; /* server return code ULESHORT */
- unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/
- unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */
- unsigned char repd_datal[4]; /* replied data length ULELONG */
- unsigned char req_pc[2]; /* PC identifier */
- unsigned char res_origin[8]; /* resource origin */
- unsigned char mac_value[8]; /* Mac Value */
- unsigned char logon_id[8]; /* Logon Identifier */
- unsigned char usage_domain[2]; /* cdx */
- unsigned char resv3[18]; /* reserved for requestor */
- unsigned short svr_namel; /* server name length ULESHORT */
- unsigned char svr_name[8]; /* server name */
-} __attribute__((packed));
-
-/**
- * The type 86 message family is associated with PCICC and PCIXCC cards.
- *
- * It contains a message header followed by a CPRB. The CPRB is
- * the same as the request CPRB, which is described above.
- *
- * If format is 1, an error condition exists and no data beyond
- * the 8-byte message header is of interest.
- *
- * The non-error message is shown below.
- *
- * Note that all reserved fields must be zeroes.
- */
-struct type86_hdr {
- unsigned char reserved1; /* 0x00 */
- unsigned char type; /* 0x86 */
- unsigned char format; /* 0x01 (error) or 0x02 (ok) */
- unsigned char reserved2; /* 0x00 */
- unsigned char reply_code; /* reply code (see above) */
- unsigned char reserved3[3]; /* 0x000000 */
-} __attribute__((packed));
-
-#define TYPE86_RSP_CODE 0x86
-#define TYPE86_FMT2 0x02
-
-struct type86_fmt2_ext {
- unsigned char reserved[4]; /* 0x00000000 */
- unsigned char apfs[4]; /* final status */
- unsigned int count1; /* length of CPRB + parameters */
- unsigned int offset1; /* offset to CPRB */
- unsigned int count2; /* 0x00000000 */
- unsigned int offset2; /* db offset 0x00000000 for PKD */
- unsigned int count3; /* 0x00000000 */
- unsigned int offset3; /* 0x00000000 */
- unsigned int count4; /* 0x00000000 */
- unsigned int offset4; /* 0x00000000 */
-} __attribute__((packed));
-
-struct function_and_rules_block {
- unsigned char function_code[2];
- unsigned short ulen;
- unsigned char only_rule[8];
-} __attribute__((packed));
-
-int zcrypt_pcicc_init(void);
-void zcrypt_pcicc_exit(void);
-
-#endif /* _ZCRYPT_PCICC_H_ */
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 33f7040d7..0ba3a2f81 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -149,12 +149,11 @@ static struct device_driver netiucv_driver = {
.pm = &netiucv_pm_ops,
};
-static int netiucv_callback_connreq(struct iucv_path *,
- u8 ipvmid[8], u8 ipuser[16]);
-static void netiucv_callback_connack(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connrej(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connsusp(struct iucv_path *, u8 ipuser[16]);
-static void netiucv_callback_connres(struct iucv_path *, u8 ipuser[16]);
+static int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *);
+static void netiucv_callback_connack(struct iucv_path *, u8 *);
+static void netiucv_callback_connrej(struct iucv_path *, u8 *);
+static void netiucv_callback_connsusp(struct iucv_path *, u8 *);
+static void netiucv_callback_connres(struct iucv_path *, u8 *);
static void netiucv_callback_rx(struct iucv_path *, struct iucv_message *);
static void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *);
@@ -556,8 +555,8 @@ static void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16])
fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn);
}
-static int netiucv_callback_connreq(struct iucv_path *path,
- u8 ipvmid[8], u8 ipuser[16])
+static int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid,
+ u8 *ipuser)
{
struct iucv_connection *conn = path->private;
struct iucv_event ev;
@@ -587,21 +586,21 @@ static int netiucv_callback_connreq(struct iucv_path *path,
return rc;
}
-static void netiucv_callback_connrej(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn);
}
-static void netiucv_callback_connsusp(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn);
}
-static void netiucv_callback_connres(struct iucv_path *path, u8 ipuser[16])
+static void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser)
{
struct iucv_connection *conn = path->private;
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index ba974a2e4..1766a20eb 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -18,6 +18,7 @@
#include <linux/bitops.h>
#include <linux/seq_file.h>
#include <linux/ethtool.h>
+#include <linux/hashtable.h>
#include <net/ipv6.h>
#include <net/if_inet6.h>
@@ -663,9 +664,7 @@ struct qeth_card_info {
char mcl_level[QETH_MCL_LENGTH + 1];
int guestlan;
int mac_bits;
- int portname_required;
int portno;
- char portname[9];
enum qeth_card_types type;
enum qeth_link_types link_type;
int is_multicast_different;
@@ -741,11 +740,17 @@ struct qeth_vlan_vid {
unsigned short vid;
};
-struct qeth_mc_mac {
- struct list_head list;
- __u8 mc_addr[MAX_ADDR_LEN];
- unsigned char mc_addrlen;
- int is_vmac;
+enum qeth_mac_disposition {
+ QETH_DISP_MAC_DELETE = 0,
+ QETH_DISP_MAC_DO_NOTHING = 1,
+ QETH_DISP_MAC_ADD = 2,
+};
+
+struct qeth_mac {
+ u8 mac_addr[OSA_ADDR_LEN];
+ u8 is_uc:1;
+ u8 disp_flag:2;
+ struct hlist_node hnode;
};
struct qeth_rx {
@@ -792,7 +797,7 @@ struct qeth_card {
spinlock_t mclock;
unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
struct list_head vid_list;
- struct list_head mc_list;
+ DECLARE_HASHTABLE(mac_htable, 4);
struct work_struct kernel_thread_starter;
spinlock_t thread_mask_lock;
unsigned long thread_start_mask;
@@ -969,6 +974,15 @@ int qeth_hw_trap(struct qeth_card *, enum qeth_diags_trap_action);
int qeth_query_ipassists(struct qeth_card *, enum qeth_prot_versions prot);
void qeth_trace_features(struct qeth_card *);
void qeth_close_dev(struct qeth_card *);
+int qeth_send_simple_setassparms(struct qeth_card *, enum qeth_ipa_funcs,
+ __u16, long);
+int qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *, __u16,
+ long,
+ int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *, unsigned long),
+ void *);
+int qeth_start_ipa_tx_checksum(struct qeth_card *);
+int qeth_set_rx_csum(struct qeth_card *, int);
/* exports for OSN */
int qeth_osn_assist(struct net_device *, void *, int);
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 5e20fba37..31ac53fa5 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1982,14 +1982,6 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
goto out;
}
-/**
- * * temporary fix for microcode bug
- * * to revert it,replace OR by AND
- * */
- if ((!QETH_IDX_NO_PORTNAME_REQUIRED(iob->data)) ||
- (card->info.type == QETH_CARD_TYPE_OSD))
- card->info.portname_required = 1;
-
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (temp != qeth_peer_func_level(card->info.func_level)) {
QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
@@ -2360,8 +2352,6 @@ static int qeth_ulp_enable(struct qeth_card *card)
&card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
&card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);
- memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data),
- card->info.portname, 9);
rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob,
qeth_ulp_enable_cb, NULL);
return rc;
@@ -2680,48 +2670,6 @@ out_qdio:
return rc;
}
-static void qeth_print_status_with_portname(struct qeth_card *card)
-{
- char dbf_text[15];
- int i;
-
- sprintf(dbf_text, "%s", card->info.portname + 1);
- for (i = 0; i < 8; i++)
- dbf_text[i] =
- (char) _ebcasc[(__u8) dbf_text[i]];
- dbf_text[8] = 0;
- dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
- "with link type %s (portname: %s)\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card),
- dbf_text);
-
-}
-
-static void qeth_print_status_no_portname(struct qeth_card *card)
-{
- if (card->info.portname[0])
- dev_info(&card->gdev->dev, "Device is a%s "
- "card%s%s%s\nwith link type %s "
- "(no portname needed by interface).\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card));
- else
- dev_info(&card->gdev->dev, "Device is a%s "
- "card%s%s%s\nwith link type %s.\n",
- qeth_get_cardname(card),
- (card->info.mcl_level[0]) ? " (level: " : "",
- (card->info.mcl_level[0]) ? card->info.mcl_level : "",
- (card->info.mcl_level[0]) ? ")" : "",
- qeth_get_cardname_short(card));
-}
-
void qeth_print_status_message(struct qeth_card *card)
{
switch (card->info.type) {
@@ -2758,10 +2706,13 @@ void qeth_print_status_message(struct qeth_card *card)
default:
memset(&card->info.mcl_level[0], 0, QETH_MCL_LENGTH + 1);
}
- if (card->info.portname_required)
- qeth_print_status_with_portname(card);
- else
- qeth_print_status_no_portname(card);
+ dev_info(&card->gdev->dev,
+ "Device is a%s card%s%s%s\nwith link type %s.\n",
+ qeth_get_cardname(card),
+ (card->info.mcl_level[0]) ? " (level: " : "",
+ (card->info.mcl_level[0]) ? card->info.mcl_level : "",
+ (card->info.mcl_level[0]) ? ")" : "",
+ qeth_get_cardname_short(card));
}
EXPORT_SYMBOL_GPL(qeth_print_status_message);
@@ -5027,13 +4978,11 @@ static void qeth_core_free_card(struct qeth_card *card)
void qeth_trace_features(struct qeth_card *card)
{
QETH_CARD_TEXT(card, 2, "features");
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa4.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.ipa6.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.supported_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->options.adp.enabled_funcs);
- QETH_CARD_TEXT_(card, 2, "%x", card->info.diagass_support);
+ QETH_CARD_HEX(card, 2, &card->options.ipa4, sizeof(card->options.ipa4));
+ QETH_CARD_HEX(card, 2, &card->options.ipa6, sizeof(card->options.ipa6));
+ QETH_CARD_HEX(card, 2, &card->options.adp, sizeof(card->options.adp));
+ QETH_CARD_HEX(card, 2, &card->info.diagass_support,
+ sizeof(card->info.diagass_support));
}
EXPORT_SYMBOL_GPL(qeth_trace_features);
@@ -5132,6 +5081,7 @@ retriable:
}
card->options.ipa4.supported_funcs = 0;
+ card->options.ipa6.supported_funcs = 0;
card->options.adp.supported_funcs = 0;
card->options.sbp.supported_funcs = 0;
card->info.diagass_support = 0;
@@ -5317,6 +5267,102 @@ no_mem:
}
EXPORT_SYMBOL_GPL(qeth_core_get_next_skb);
+static int qeth_setassparms_cb(struct qeth_card *card,
+ struct qeth_reply *reply, unsigned long data)
+{
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "defadpcb");
+
+ cmd = (struct qeth_ipa_cmd *) data;
+ if (cmd->hdr.return_code == 0) {
+ cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV4)
+ card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+ if (cmd->hdr.prot_version == QETH_PROT_IPV6)
+ card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.csum_mask = cmd->data.setassparms.data.flags_32bit;
+ QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask);
+ }
+ if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM &&
+ cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) {
+ card->info.tx_csum_mask =
+ cmd->data.setassparms.data.flags_32bit;
+ QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask);
+ }
+
+ return 0;
+}
+
+static struct qeth_cmd_buffer *qeth_get_setassparms_cmd(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, __u16 len,
+ enum qeth_prot_versions prot)
+{
+ struct qeth_cmd_buffer *iob;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "getasscm");
+ iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot);
+
+ if (iob) {
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ cmd->data.setassparms.hdr.assist_no = ipa_func;
+ cmd->data.setassparms.hdr.length = 8 + len;
+ cmd->data.setassparms.hdr.command_code = cmd_code;
+ cmd->data.setassparms.hdr.return_code = 0;
+ cmd->data.setassparms.hdr.seq_no = 0;
+ }
+
+ return iob;
+}
+
+int qeth_send_setassparms(struct qeth_card *card,
+ struct qeth_cmd_buffer *iob, __u16 len, long data,
+ int (*reply_cb)(struct qeth_card *,
+ struct qeth_reply *, unsigned long),
+ void *reply_param)
+{
+ int rc;
+ struct qeth_ipa_cmd *cmd;
+
+ QETH_CARD_TEXT(card, 4, "sendassp");
+
+ cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+ if (len <= sizeof(__u32))
+ cmd->data.setassparms.data.flags_32bit = (__u32) data;
+ else /* (len > sizeof(__u32)) */
+ memcpy(&cmd->data.setassparms.data, (void *) data, len);
+
+ rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_setassparms);
+
+int qeth_send_simple_setassparms(struct qeth_card *card,
+ enum qeth_ipa_funcs ipa_func,
+ __u16 cmd_code, long data)
+{
+ int rc;
+ int length = 0;
+ struct qeth_cmd_buffer *iob;
+
+ QETH_CARD_TEXT(card, 4, "simassp4");
+ if (data)
+ length = sizeof(__u32);
+ iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code,
+ length, QETH_PROT_IPV4);
+ if (!iob)
+ return -ENOMEM;
+ rc = qeth_send_setassparms(card, iob, length, data,
+ qeth_setassparms_cb, NULL);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_send_simple_setassparms);
+
static void qeth_unregister_dbf_views(void)
{
int x;
@@ -6003,6 +6049,75 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings);
+static int qeth_send_checksum_command(struct qeth_card *card)
+{
+ int rc;
+
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.csum_mask);
+ if (rc) {
+ dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
+ return rc;
+ }
+ return 0;
+}
+
+int qeth_set_rx_csum(struct qeth_card *card, int on)
+{
+ int rc;
+
+ if (on) {
+ rc = qeth_send_checksum_command(card);
+ if (rc)
+ return -EIO;
+ dev_info(&card->gdev->dev,
+ "HW Checksumming (inbound) enabled\n");
+ } else {
+ rc = qeth_send_simple_setassparms(card,
+ IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
+ if (rc)
+ return -EIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qeth_set_rx_csum);
+
+int qeth_start_ipa_tx_checksum(struct qeth_card *card)
+{
+ int rc = 0;
+
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ return rc;
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_START, 0);
+ if (rc)
+ goto err_out;
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
+ IPA_CMD_ASS_ENABLE,
+ card->info.tx_csum_mask);
+ if (rc)
+ goto err_out;
+
+ dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
+ return rc;
+err_out:
+ dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
+ "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
+ return rc;
+}
+EXPORT_SYMBOL_GPL(qeth_start_ipa_tx_checksum);
+
static int __init qeth_core_init(void)
{
int rc;
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index 423bec56c..e6e5b9671 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -153,52 +153,17 @@ static DEVICE_ATTR(portno, 0644, qeth_dev_portno_show, qeth_dev_portno_store);
static ssize_t qeth_dev_portname_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct qeth_card *card = dev_get_drvdata(dev);
- char portname[9] = {0, };
-
- if (!card)
- return -EINVAL;
-
- if (card->info.portname_required) {
- memcpy(portname, card->info.portname + 1, 8);
- EBCASC(portname, 8);
- return sprintf(buf, "%s\n", portname);
- } else
- return sprintf(buf, "no portname required\n");
+ return sprintf(buf, "no portname required\n");
}
static ssize_t qeth_dev_portname_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- char *tmp;
- int i, rc = 0;
-
- if (!card)
- return -EINVAL;
-
- mutex_lock(&card->conf_mutex);
- if ((card->state != CARD_STATE_DOWN) &&
- (card->state != CARD_STATE_RECOVER)) {
- rc = -EPERM;
- goto out;
- }
- tmp = strsep((char **) &buf, "\n");
- if ((strlen(tmp) > 8) || (strlen(tmp) == 0)) {
- rc = -EINVAL;
- goto out;
- }
-
- card->info.portname[0] = strlen(tmp);
- /* for beauty reasons */
- for (i = 1; i < 9; i++)
- card->info.portname[i] = ' ';
- strcpy(card->info.portname + 1, tmp);
- ASCEBC(card->info.portname + 1, 8);
-out:
- mutex_unlock(&card->conf_mutex);
- return rc ? rc : count;
+ dev_warn_once(&card->gdev->dev,
+ "portname is deprecated and is ignored\n");
+ return count;
}
static DEVICE_ATTR(portname, 0644, qeth_dev_portname_show,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index a8556692f..8f1b091e1 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -19,7 +19,9 @@
#include <linux/mii.h>
#include <linux/ip.h>
#include <linux/list.h>
-
+#include <linux/hash.h>
+#include <linux/hashtable.h>
+#include <linux/string.h>
#include "qeth_core.h"
#include "qeth_l2.h"
@@ -28,7 +30,7 @@ static int qeth_l2_stop(struct net_device *);
static int qeth_l2_send_delmac(struct qeth_card *, __u8 *);
static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *,
enum qeth_ipa_cmds);
-static void qeth_l2_set_multicast_list(struct net_device *);
+static void qeth_l2_set_rx_mode(struct net_device *);
static int qeth_l2_recover(void *);
static void qeth_bridgeport_query_support(struct qeth_card *card);
static void qeth_bridge_state_change(struct qeth_card *card,
@@ -193,49 +195,44 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
return rc;
}
-static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac)
+static inline u32 qeth_l2_mac_hash(const u8 *addr)
{
- struct qeth_mc_mac *mc;
- int rc;
-
- mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
+ return get_unaligned((u32 *)(&addr[2]));
+}
- if (!mc)
- return;
+static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac)
+{
- memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
- mc->mc_addrlen = OSA_ADDR_LEN;
- mc->is_vmac = vmac;
+ int rc;
- if (vmac) {
+ if (mac->is_uc) {
rc = qeth_setdel_makerc(card,
- qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC));
+ qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_SETVMAC));
} else {
rc = qeth_setdel_makerc(card,
- qeth_l2_send_setgroupmac(card, mac));
+ qeth_l2_send_setgroupmac(card, mac->mac_addr));
}
-
- if (!rc)
- list_add_tail(&mc->list, &card->mc_list);
- else
- kfree(mc);
+ return rc;
}
-static void qeth_l2_del_all_mc(struct qeth_card *card, int del)
+static void qeth_l2_del_all_macs(struct qeth_card *card, int del)
{
- struct qeth_mc_mac *mc, *tmp;
+ struct qeth_mac *mac;
+ struct hlist_node *tmp;
+ int i;
spin_lock_bh(&card->mclock);
- list_for_each_entry_safe(mc, tmp, &card->mc_list, list) {
+ hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
if (del) {
- if (mc->is_vmac)
- qeth_l2_send_setdelmac(card, mc->mc_addr,
- IPA_CMD_DELVMAC);
+ if (mac->is_uc)
+ qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_DELVMAC);
else
- qeth_l2_send_delgroupmac(card, mc->mc_addr);
+ qeth_l2_send_delgroupmac(card, mac->mac_addr);
}
- list_del(&mc->list);
- kfree(mc);
+ hash_del(&mac->hnode);
+ kfree(mac);
}
spin_unlock_bh(&card->mclock);
}
@@ -252,6 +249,23 @@ static inline int qeth_l2_get_cast_type(struct qeth_card *card,
return RTN_UNSPEC;
}
+static inline void qeth_l2_hdr_csum(struct qeth_card *card,
+ struct qeth_hdr *hdr, struct sk_buff *skb)
+{
+ struct iphdr *iph = ip_hdr(skb);
+
+ /* tcph->check contains already the pseudo hdr checksum
+ * so just set the header flags
+ */
+ if (iph->protocol == IPPROTO_UDP)
+ hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_UDP;
+ hdr->hdr.l2.flags[1] |= QETH_HDR_EXT_CSUM_TRANSP_REQ |
+ QETH_HDR_EXT_CSUM_HDR_REQ;
+ iph->check = 0;
+ if (card->options.performance_stats)
+ card->perf_stats.tx_csum++;
+}
+
static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
struct sk_buff *skb, int cast_type)
{
@@ -386,10 +400,42 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev,
rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
kfree(tmpid);
}
- qeth_l2_set_multicast_list(card->dev);
+ qeth_l2_set_rx_mode(card->dev);
return rc;
}
+static netdev_features_t qeth_l2_fix_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+
+ QETH_DBF_TEXT(SETUP, 2, "fixfeat");
+ if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
+ features &= ~NETIF_F_IP_CSUM;
+ if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ features &= ~NETIF_F_RXCSUM;
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+ return features;
+}
+
+static int qeth_l2_set_features(struct net_device *dev,
+ netdev_features_t features)
+{
+ struct qeth_card *card = dev->ml_priv;
+ netdev_features_t changed = dev->features ^ features;
+
+ QETH_DBF_TEXT(SETUP, 2, "setfeat");
+ QETH_DBF_HEX(SETUP, 2, &features, sizeof(features));
+
+ if (card->state == CARD_STATE_DOWN ||
+ card->state == CARD_STATE_RECOVER)
+ return 0;
+
+ if (!(changed & NETIF_F_RXCSUM))
+ return 0;
+ return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+}
+
static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
{
QETH_DBF_TEXT(SETUP , 2, "stopcard");
@@ -411,7 +457,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode)
card->state = CARD_STATE_SOFTSETUP;
}
if (card->state == CARD_STATE_SOFTSETUP) {
- qeth_l2_del_all_mc(card, 0);
+ qeth_l2_del_all_macs(card, 0);
qeth_clear_ipacmd_list(card);
card->state = CARD_STATE_HARDSETUP;
}
@@ -450,11 +496,19 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card,
case QETH_HEADER_TYPE_LAYER2:
skb->pkt_type = PACKET_HOST;
skb->protocol = eth_type_trans(skb, skb->dev);
- skb->ip_summed = CHECKSUM_NONE;
+ if ((card->dev->features & NETIF_F_RXCSUM)
+ && ((hdr->hdr.l2.flags[1] &
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
+ (QETH_HDR_EXT_CSUM_HDR_REQ |
+ QETH_HDR_EXT_CSUM_TRANSP_REQ)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb->ip_summed = CHECKSUM_NONE;
if (skb->protocol == htons(ETH_P_802_2))
*((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
len = skb->len;
- netif_receive_skb(skb);
+ napi_gro_receive(&card->napi, skb);
break;
case QETH_HEADER_TYPE_OSN:
if (card->info.type == QETH_CARD_TYPE_OSN) {
@@ -711,29 +765,91 @@ static void qeth_promisc_to_bridge(struct qeth_card *card)
card->options.sbp.role = role;
card->info.promisc_mode = promisc_mode;
}
+
}
+/* New MAC address is added to the hash table and marked to be written on card
+ * only if there is not in the hash table storage already
+ *
+*/
+static void
+qeth_l2_add_mac(struct qeth_card *card, struct netdev_hw_addr *ha, u8 is_uc)
+{
+ struct qeth_mac *mac;
-static void qeth_l2_set_multicast_list(struct net_device *dev)
+ hash_for_each_possible(card->mac_htable, mac, hnode,
+ qeth_l2_mac_hash(ha->addr)) {
+ if (is_uc == mac->is_uc &&
+ !memcmp(ha->addr, mac->mac_addr, OSA_ADDR_LEN)) {
+ mac->disp_flag = QETH_DISP_MAC_DO_NOTHING;
+ return;
+ }
+ }
+
+ mac = kzalloc(sizeof(struct qeth_mac), GFP_ATOMIC);
+
+ if (!mac)
+ return;
+
+ memcpy(mac->mac_addr, ha->addr, OSA_ADDR_LEN);
+ mac->is_uc = is_uc;
+ mac->disp_flag = QETH_DISP_MAC_ADD;
+
+ hash_add(card->mac_htable, &mac->hnode,
+ qeth_l2_mac_hash(mac->mac_addr));
+
+}
+
+static void qeth_l2_set_rx_mode(struct net_device *dev)
{
struct qeth_card *card = dev->ml_priv;
struct netdev_hw_addr *ha;
+ struct qeth_mac *mac;
+ struct hlist_node *tmp;
+ int i;
+ int rc;
if (card->info.type == QETH_CARD_TYPE_OSN)
- return ;
+ return;
QETH_CARD_TEXT(card, 3, "setmulti");
if (qeth_threads_running(card, QETH_RECOVER_THREAD) &&
(card->state != CARD_STATE_UP))
return;
- qeth_l2_del_all_mc(card, 1);
+
spin_lock_bh(&card->mclock);
+
netdev_for_each_mc_addr(ha, dev)
- qeth_l2_add_mc(card, ha->addr, 0);
+ qeth_l2_add_mac(card, ha, 0);
netdev_for_each_uc_addr(ha, dev)
- qeth_l2_add_mc(card, ha->addr, 1);
+ qeth_l2_add_mac(card, ha, 1);
+
+ hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) {
+ if (mac->disp_flag == QETH_DISP_MAC_DELETE) {
+ if (!mac->is_uc)
+ rc = qeth_l2_send_delgroupmac(card,
+ mac->mac_addr);
+ else {
+ rc = qeth_l2_send_setdelmac(card, mac->mac_addr,
+ IPA_CMD_DELVMAC);
+ }
+
+ hash_del(&mac->hnode);
+ kfree(mac);
+
+ } else if (mac->disp_flag == QETH_DISP_MAC_ADD) {
+ rc = qeth_l2_write_mac(card, mac);
+ if (rc) {
+ hash_del(&mac->hnode);
+ kfree(mac);
+ } else
+ mac->disp_flag = QETH_DISP_MAC_DELETE;
+ } else
+ mac->disp_flag = QETH_DISP_MAC_DELETE;
+ }
spin_unlock_bh(&card->mclock);
+
if (qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
qeth_setadp_promisc_mode(card);
else
@@ -803,6 +919,8 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
sizeof(struct qeth_hdr));
skb_set_mac_header(new_skb, sizeof(struct qeth_hdr));
qeth_l2_fill_header(card, hdr, new_skb, cast_type);
+ if (new_skb->ip_summed == CHECKSUM_PARTIAL)
+ qeth_l2_hdr_csum(card, hdr, new_skb);
}
}
@@ -915,7 +1033,7 @@ static int qeth_l2_probe_device(struct ccwgroup_device *gdev)
qeth_l2_create_device_attributes(&gdev->dev);
INIT_LIST_HEAD(&card->vid_list);
- INIT_LIST_HEAD(&card->mc_list);
+ hash_init(card->mac_htable);
card->options.layer2 = 1;
card->info.hwtrap = 0;
return 0;
@@ -961,13 +1079,15 @@ static const struct net_device_ops qeth_l2_netdev_ops = {
.ndo_get_stats = qeth_get_stats,
.ndo_start_xmit = qeth_l2_hard_start_xmit,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_rx_mode = qeth_l2_set_multicast_list,
+ .ndo_set_rx_mode = qeth_l2_set_rx_mode,
.ndo_do_ioctl = qeth_l2_do_ioctl,
.ndo_set_mac_address = qeth_l2_set_mac_address,
.ndo_change_mtu = qeth_change_mtu,
.ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = qeth_l2_vlan_rx_kill_vid,
.ndo_tx_timeout = qeth_tx_timeout,
+ .ndo_fix_features = qeth_l2_fix_features,
+ .ndo_set_features = qeth_l2_set_features
};
static int qeth_l2_setup_netdev(struct qeth_card *card)
@@ -997,6 +1117,11 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
(card->info.type != QETH_CARD_TYPE_OSN) ?
&qeth_l2_ethtool_ops : &qeth_l2_osn_ops;
card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+ if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) {
+ card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM;
+ /* Turn on RX offloading per default */
+ card->dev->features |= NETIF_F_RXCSUM;
+ }
card->info.broadcast_capable = 1;
qeth_l2_request_initial_mac(card);
SET_NETDEV_DEV(card->dev, &card->gdev->dev);
@@ -1004,6 +1129,17 @@ static int qeth_l2_setup_netdev(struct qeth_card *card)
return register_netdev(card->dev);
}
+static int qeth_l2_start_ipassists(struct qeth_card *card)
+{
+ /* configure isolation level */
+ if (qeth_set_access_ctrl_online(card, 0))
+ return -ENODEV;
+ if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
+ qeth_set_rx_csum(card, 1);
+ qeth_start_ipa_tx_checksum(card);
+ return 0;
+}
+
static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
{
struct qeth_card *card = dev_get_drvdata(&gdev->dev);
@@ -1069,12 +1205,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
contin:
if ((card->info.type == QETH_CARD_TYPE_OSD) ||
(card->info.type == QETH_CARD_TYPE_OSX)) {
- /* configure isolation level */
- rc = qeth_set_access_ctrl_online(card, 0);
- if (rc) {
- rc = -ENODEV;
+ if (qeth_l2_start_ipassists(card))
goto out_remove;
- }
}
if (card->info.type != QETH_CARD_TYPE_OSN &&
@@ -1106,7 +1238,7 @@ contin:
rtnl_unlock();
}
/* this also sets saved unicast addresses */
- qeth_l2_set_multicast_list(card->dev);
+ qeth_l2_set_rx_mode(card->dev);
}
/* let user_space know that device is online */
kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE);
@@ -1452,8 +1584,8 @@ static void qeth_bridge_emit_host_event(struct qeth_card *card,
env[i] = str[i]; i++;
}
if (code & IPA_ADDR_CHANGE_CODE_MACADDR) {
- snprintf(str[i], sizeof(str[i]), "MAC=%pM6",
- &addr_lnid->mac);
+ snprintf(str[i], sizeof(str[i]), "MAC=%pM",
+ addr_lnid->mac);
env[i] = str[i]; i++;
}
snprintf(str[i], sizeof(str[i]), "NTOK_BUSID=%x.%x.%04x",
diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c
index 52673cd1d..692db49e3 100644
--- a/drivers/s390/net/qeth_l2_sys.c
+++ b/drivers/s390/net/qeth_l2_sys.c
@@ -109,7 +109,7 @@ static ssize_t qeth_bridge_port_state_show(struct device *dev,
return qeth_bridge_port_role_state_show(dev, attr, buf, 1);
}
-static DEVICE_ATTR(bridge_state, 0644, qeth_bridge_port_state_show,
+static DEVICE_ATTR(bridge_state, 0444, qeth_bridge_port_state_show,
NULL);
static ssize_t qeth_bridgeport_hostnotification_show(struct device *dev,
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index a1aaa36e9..543960e96 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -1065,27 +1065,6 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd(
return iob;
}
-static int qeth_l3_send_setassparms(struct qeth_card *card,
- struct qeth_cmd_buffer *iob, __u16 len, long data,
- int (*reply_cb)(struct qeth_card *, struct qeth_reply *,
- unsigned long),
- void *reply_param)
-{
- int rc;
- struct qeth_ipa_cmd *cmd;
-
- QETH_CARD_TEXT(card, 4, "sendassp");
-
- cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
- if (len <= sizeof(__u32))
- cmd->data.setassparms.data.flags_32bit = (__u32) data;
- else /* (len > sizeof(__u32)) */
- memcpy(&cmd->data.setassparms.data, (void *) data, len);
-
- rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
- return rc;
-}
-
#ifdef CONFIG_QETH_IPV6
static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
enum qeth_ipa_funcs ipa_func, __u16 cmd_code)
@@ -1098,31 +1077,12 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card,
0, QETH_PROT_IPV6);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob, 0, 0,
+ rc = qeth_send_setassparms(card, iob, 0, 0,
qeth_l3_default_setassparms_cb, NULL);
return rc;
}
#endif
-static int qeth_l3_send_simple_setassparms(struct qeth_card *card,
- enum qeth_ipa_funcs ipa_func, __u16 cmd_code, long data)
-{
- int rc;
- int length = 0;
- struct qeth_cmd_buffer *iob;
-
- QETH_CARD_TEXT(card, 4, "simassp4");
- if (data)
- length = sizeof(__u32);
- iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code,
- length, QETH_PROT_IPV4);
- if (!iob)
- return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob, length, data,
- qeth_l3_default_setassparms_cb, NULL);
- return rc;
-}
-
static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
{
int rc;
@@ -1135,8 +1095,8 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
QETH_CARD_IFNAME(card));
return 0;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
- IPA_CMD_ASS_START, 0);
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
"Starting ARP processing support for %s failed\n",
@@ -1158,7 +1118,7 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
+ rc = qeth_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1183,7 +1143,7 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
+ rc = qeth_send_simple_setassparms(card, IPA_SOURCE_MAC,
IPA_CMD_ASS_START, 0);
if (rc)
dev_warn(&card->gdev->dev,
@@ -1204,7 +1164,7 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
+ rc = qeth_send_simple_setassparms(card, IPA_VLAN_PRIO,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1229,7 +1189,7 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
+ rc = qeth_send_simple_setassparms(card, IPA_MULTICASTING,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1259,7 +1219,7 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
QETH_CARD_IFNAME(card));
return rc;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
+ rc = qeth_send_simple_setassparms(card, IPA_IPV6,
IPA_CMD_ASS_START, 3);
if (rc) {
dev_err(&card->gdev->dev,
@@ -1319,7 +1279,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
rc = -EOPNOTSUPP;
goto out;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_START, 0);
if (rc) {
dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
@@ -1327,7 +1287,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
goto out;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_CONFIGURE, 1);
if (rc) {
dev_warn(&card->gdev->dev,
@@ -1337,7 +1297,7 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
}
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
dev_info(&card->gdev->dev, "Broadcast enabled\n");
- rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
+ rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_ENABLE, 1);
if (rc) {
dev_warn(&card->gdev->dev, "Setting up broadcast echo "
@@ -1353,84 +1313,18 @@ out:
return rc;
}
-static int qeth_l3_send_checksum_command(struct qeth_card *card)
-{
- int rc;
-
- rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
- if (rc) {
- dev_warn(&card->gdev->dev, "Starting HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
- return rc;
- }
- rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE,
- card->info.csum_mask);
- if (rc) {
- dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
- "failed, using SW checksumming\n",
- QETH_CARD_IFNAME(card));
- return rc;
- }
- return 0;
-}
-
-static int qeth_l3_set_rx_csum(struct qeth_card *card, int on)
-{
- int rc = 0;
-
- if (on) {
- rc = qeth_l3_send_checksum_command(card);
- if (rc)
- return -EIO;
- dev_info(&card->gdev->dev,
- "HW Checksumming (inbound) enabled\n");
- } else {
- rc = qeth_l3_send_simple_setassparms(card,
- IPA_INBOUND_CHECKSUM, IPA_CMD_ASS_STOP, 0);
- if (rc)
- return -EIO;
- }
-
- return 0;
-}
-
-static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_checksum(struct qeth_card *card)
{
QETH_CARD_TEXT(card, 3, "strtcsum");
-
- if (card->dev->features & NETIF_F_RXCSUM) {
- rtnl_lock();
- /* force set_features call */
- card->dev->features &= ~NETIF_F_RXCSUM;
- netdev_update_features(card->dev);
- rtnl_unlock();
- }
- return 0;
+ if (qeth_is_supported(card, IPA_INBOUND_CHECKSUM)
+ && (card->dev->features & NETIF_F_RXCSUM))
+ qeth_set_rx_csum(card, 1);
}
-static int qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
+static void qeth_l3_start_ipa_tx_checksum(struct qeth_card *card)
{
- int rc = 0;
-
- if (!qeth_is_supported(card, IPA_OUTBOUND_CHECKSUM))
- return rc;
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_START, 0);
- if (rc)
- goto err_out;
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_CHECKSUM,
- IPA_CMD_ASS_ENABLE, card->info.tx_csum_mask);
- if (rc)
- goto err_out;
- dev_info(&card->gdev->dev, "HW TX Checksumming enabled\n");
- return rc;
-err_out:
- dev_warn(&card->gdev->dev, "Enabling HW TX checksumming for %s "
- "failed, using SW TX checksumming\n", QETH_CARD_IFNAME(card));
- return rc;
+ QETH_CARD_TEXT(card, 3, "strttxcs");
+ qeth_start_ipa_tx_checksum(card);
}
static int qeth_l3_start_ipa_tso(struct qeth_card *card)
@@ -1445,8 +1339,8 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
} else {
- rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
- IPA_CMD_ASS_START, 0);
+ rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+ IPA_CMD_ASS_START, 0);
if (rc)
dev_warn(&card->gdev->dev, "Starting outbound TCP "
"segmentation offload for %s failed\n",
@@ -1950,7 +1844,6 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card,
skb->ip_summed = CHECKSUM_NONE;
} else
skb->ip_summed = CHECKSUM_NONE;
-
return is_vlan;
}
@@ -2287,7 +2180,7 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_SET_NO_ENTRIES,
no_entries);
if (rc) {
@@ -2552,7 +2445,7 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob,
+ rc = qeth_send_setassparms(card, iob,
sizeof(struct qeth_arp_cache_entry),
(unsigned long) entry,
qeth_l3_default_setassparms_cb, NULL);
@@ -2593,7 +2486,7 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
QETH_PROT_IPV4);
if (!iob)
return -ENOMEM;
- rc = qeth_l3_send_setassparms(card, iob,
+ rc = qeth_send_setassparms(card, iob,
12, (unsigned long)buf,
qeth_l3_default_setassparms_cb, NULL);
if (rc) {
@@ -2624,7 +2517,7 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
return -EOPNOTSUPP;
}
- rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
+ rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
if (rc) {
tmp = rc;
@@ -3187,7 +3080,6 @@ static netdev_features_t qeth_l3_fix_features(struct net_device *dev,
features &= ~NETIF_F_TSO;
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM))
features &= ~NETIF_F_RXCSUM;
-
return features;
}
@@ -3204,7 +3096,7 @@ static int qeth_l3_set_features(struct net_device *dev,
card->state == CARD_STATE_RECOVER)
return 0;
- return qeth_l3_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
+ return qeth_set_rx_csum(card, features & NETIF_F_RXCSUM ? 1 : 0);
}
static const struct ethtool_ops qeth_l3_ethtool_ops = {
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index d8f990b6b..a851d34c6 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -49,7 +49,7 @@ static DEFINE_SPINLOCK(smsg_list_lock);
static LIST_HEAD(smsg_list);
static int iucv_path_connected;
-static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
+static int smsg_path_pending(struct iucv_path *, u8 *, u8 *);
static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
static struct iucv_handler smsg_handler = {
@@ -57,8 +57,7 @@ static struct iucv_handler smsg_handler = {
.message_pending = smsg_message_pending,
};
-static int smsg_path_pending(struct iucv_path *path, u8 ipvmid[8],
- u8 ipuser[16])
+static int smsg_path_pending(struct iucv_path *path, u8 *ipvmid, u8 *ipuser)
{
if (strncmp(ipvmid, "*MSG ", 8) != 0)
return -EINVAL;
diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c
index e9fae30fa..1b831598d 100644
--- a/drivers/s390/virtio/virtio_ccw.c
+++ b/drivers/s390/virtio/virtio_ccw.c
@@ -28,6 +28,7 @@
#include <linux/io.h>
#include <linux/kvm_para.h>
#include <linux/notifier.h>
+#include <asm/diag.h>
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/cio.h>
@@ -366,9 +367,9 @@ static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
kfree(thinint_area);
}
-static inline long do_kvm_notify(struct subchannel_id schid,
- unsigned long queue_index,
- long cookie)
+static inline long __do_kvm_notify(struct subchannel_id schid,
+ unsigned long queue_index,
+ long cookie)
{
register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY;
register struct subchannel_id __schid asm("2") = schid;
@@ -383,6 +384,14 @@ static inline long do_kvm_notify(struct subchannel_id schid,
return __rc;
}
+static inline long do_kvm_notify(struct subchannel_id schid,
+ unsigned long queue_index,
+ long cookie)
+{
+ diag_stat_inc(DIAG_STAT_X500);
+ return __do_kvm_notify(schid, queue_index, cookie);
+}
+
static bool virtio_ccw_kvm_notify(struct virtqueue *vq)
{
struct virtio_ccw_vq_info *info = vq->priv;
@@ -975,6 +984,36 @@ static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev,
return vq;
}
+static void virtio_ccw_check_activity(struct virtio_ccw_device *vcdev,
+ __u32 activity)
+{
+ if (vcdev->curr_io & activity) {
+ switch (activity) {
+ case VIRTIO_CCW_DOING_READ_FEAT:
+ case VIRTIO_CCW_DOING_WRITE_FEAT:
+ case VIRTIO_CCW_DOING_READ_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_CONFIG:
+ case VIRTIO_CCW_DOING_WRITE_STATUS:
+ case VIRTIO_CCW_DOING_SET_VQ:
+ case VIRTIO_CCW_DOING_SET_IND:
+ case VIRTIO_CCW_DOING_SET_CONF_IND:
+ case VIRTIO_CCW_DOING_RESET:
+ case VIRTIO_CCW_DOING_READ_VQ_CONF:
+ case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
+ case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
+ vcdev->curr_io &= ~activity;
+ wake_up(&vcdev->wait_q);
+ break;
+ default:
+ /* don't know what to do... */
+ dev_warn(&vcdev->cdev->dev,
+ "Suspicious activity '%08x'\n", activity);
+ WARN_ON(1);
+ break;
+ }
+ }
+}
+
static void virtio_ccw_int_handler(struct ccw_device *cdev,
unsigned long intparm,
struct irb *irb)
@@ -986,6 +1025,12 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
if (!vcdev)
return;
+ if (IS_ERR(irb)) {
+ vcdev->err = PTR_ERR(irb);
+ virtio_ccw_check_activity(vcdev, activity);
+ /* Don't poke around indicators, something's wrong. */
+ return;
+ }
/* Check if it's a notification from the host. */
if ((intparm == 0) &&
(scsw_stctl(&irb->scsw) ==
@@ -1001,31 +1046,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
/* Map everything else to -EIO. */
vcdev->err = -EIO;
}
- if (vcdev->curr_io & activity) {
- switch (activity) {
- case VIRTIO_CCW_DOING_READ_FEAT:
- case VIRTIO_CCW_DOING_WRITE_FEAT:
- case VIRTIO_CCW_DOING_READ_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_CONFIG:
- case VIRTIO_CCW_DOING_WRITE_STATUS:
- case VIRTIO_CCW_DOING_SET_VQ:
- case VIRTIO_CCW_DOING_SET_IND:
- case VIRTIO_CCW_DOING_SET_CONF_IND:
- case VIRTIO_CCW_DOING_RESET:
- case VIRTIO_CCW_DOING_READ_VQ_CONF:
- case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
- case VIRTIO_CCW_DOING_SET_VIRTIO_REV:
- vcdev->curr_io &= ~activity;
- wake_up(&vcdev->wait_q);
- break;
- default:
- /* don't know what to do... */
- dev_warn(&cdev->dev, "Suspicious activity '%08x'\n",
- activity);
- WARN_ON(1);
- break;
- }
- }
+ virtio_ccw_check_activity(vcdev, activity);
for_each_set_bit(i, &vcdev->indicators,
sizeof(vcdev->indicators) * BITS_PER_BYTE) {
/* The bit clear must happen before the vring kick. */