aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/Kconfig10
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/hvc_iucv.c850
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/block/dasd_devmap.c19
-rw-r--r--drivers/s390/block/dasd_eckd.c2
-rw-r--r--drivers/s390/block/dasd_proc.c28
-rw-r--r--drivers/s390/block/dcssblk.c77
-rw-r--r--drivers/s390/block/xpram.c41
-rw-r--r--drivers/s390/char/monreader.c41
-rw-r--r--drivers/s390/char/monwriter.c5
-rw-r--r--drivers/s390/char/sclp_cmd.c29
-rw-r--r--drivers/s390/char/sclp_config.c10
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c12
-rw-r--r--drivers/s390/char/sclp_sdias.c18
-rw-r--r--drivers/s390/char/sclp_vt220.c33
-rw-r--r--drivers/s390/char/vmcp.c11
-rw-r--r--drivers/s390/char/vmlogrdr.c26
-rw-r--r--drivers/s390/char/vmur.c15
-rw-r--r--drivers/s390/char/zcore.c14
-rw-r--r--drivers/s390/cio/blacklist.c14
-rw-r--r--drivers/s390/cio/ccwgroup.c26
-rw-r--r--drivers/s390/cio/chsc.c8
-rw-r--r--drivers/s390/cio/chsc_sch.c2
-rw-r--r--drivers/s390/cio/cio.c245
-rw-r--r--drivers/s390/cio/cio.h18
-rw-r--r--drivers/s390/cio/cmf.c63
-rw-r--r--drivers/s390/cio/css.c12
-rw-r--r--drivers/s390/cio/device.c237
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c46
-rw-r--r--drivers/s390/cio/device_pgid.c2
-rw-r--r--drivers/s390/cio/device_status.c4
-rw-r--r--drivers/s390/cio/qdio.h33
-rw-r--r--drivers/s390/cio/qdio_debug.c104
-rw-r--r--drivers/s390/cio/qdio_debug.h112
-rw-r--r--drivers/s390/cio/qdio_main.c648
-rw-r--r--drivers/s390/cio/qdio_perf.c8
-rw-r--r--drivers/s390/cio/qdio_perf.h5
-rw-r--r--drivers/s390/cio/qdio_setup.c145
-rw-r--r--drivers/s390/cio/qdio_thinint.c29
-rw-r--r--drivers/s390/crypto/ap_bus.c212
-rw-r--r--drivers/s390/crypto/ap_bus.h6
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c10
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c10
-rw-r--r--drivers/s390/net/ctcm_fsms.c46
-rw-r--r--drivers/s390/net/ctcm_main.c72
-rw-r--r--drivers/s390/net/ctcm_main.h6
-rw-r--r--drivers/s390/net/ctcm_mpc.c15
-rw-r--r--drivers/s390/net/ctcm_sysfs.c3
-rw-r--r--drivers/s390/net/lcs.c92
-rw-r--r--drivers/s390/net/netiucv.c64
-rw-r--r--drivers/s390/net/qeth_core.h9
-rw-r--r--drivers/s390/net/qeth_core_main.c176
-rw-r--r--drivers/s390/net/qeth_l2_main.c37
-rw-r--r--drivers/s390/net/qeth_l3_main.c211
-rw-r--r--drivers/s390/scsi/zfcp_aux.c28
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c3
-rw-r--r--drivers/s390/scsi/zfcp_cfdc.c3
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c3
-rw-r--r--drivers/s390/scsi/zfcp_def.h2
-rw-r--r--drivers/s390/scsi/zfcp_erp.c10
-rw-r--r--drivers/s390/scsi/zfcp_fc.c3
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c3
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c3
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c3
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c3
-rw-r--r--drivers/s390/sysinfo.c127
70 files changed, 2751 insertions, 1437 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 43d6ba83a19..8783457b93d 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -622,6 +622,16 @@ config HVC_BEAT
help
Toshiba's Cell Reference Set Beat Console device driver
+config HVC_IUCV
+ bool "z/VM IUCV Hypervisor console support (VM only)"
+ depends on S390
+ select HVC_DRIVER
+ select IUCV
+ default y
+ help
+ This driver provides a Hypervisor console (HVC) back-end to access
+ a Linux (console) terminal via a z/VM IUCV communication path.
+
config HVC_XEN
bool "Xen Hypervisor Console support"
depends on XEN
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 438f71317c5..36151bae0d7 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_HVC_BEAT) += hvc_beat.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
+obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
new file mode 100644
index 00000000000..5ea7d7713fc
--- /dev/null
+++ b/drivers/char/hvc_iucv.c
@@ -0,0 +1,850 @@
+/*
+ * hvc_iucv.c - z/VM IUCV back-end for the Hypervisor Console (HVC)
+ *
+ * This back-end for HVC provides terminal access via
+ * z/VM IUCV communication paths.
+ *
+ * Copyright IBM Corp. 2008.
+ *
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ */
+#define KMSG_COMPONENT "hvc_iucv"
+
+#include <linux/types.h>
+#include <asm/ebcdic.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <net/iucv/iucv.h>
+
+#include "hvc_console.h"
+
+
+/* HVC backend for z/VM IUCV */
+#define HVC_IUCV_MAGIC 0xc9e4c3e5
+#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS
+#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4)
+
+/* IUCV TTY message */
+#define MSG_VERSION 0x02 /* Message version */
+#define MSG_TYPE_ERROR 0x01 /* Error message */
+#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */
+#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */
+#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */
+#define MSG_TYPE_DATA 0x10 /* Terminal data */
+
+#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data))
+struct iucv_tty_msg {
+ u8 version; /* Message version */
+ u8 type; /* Message type */
+#define MSG_MAX_DATALEN (~(u16)0)
+ u16 datalen; /* Payload length */
+ u8 data[]; /* Payload buffer */
+} __attribute__((packed));
+
+enum iucv_state_t {
+ IUCV_DISCONN = 0,
+ IUCV_CONNECTED = 1,
+ IUCV_SEVERED = 2,
+};
+
+enum tty_state_t {
+ TTY_CLOSED = 0,
+ TTY_OPENED = 1,
+};
+
+struct hvc_iucv_private {
+ struct hvc_struct *hvc; /* HVC console struct reference */
+ u8 srv_name[8]; /* IUCV service name (ebcdic) */
+ enum iucv_state_t iucv_state; /* IUCV connection status */
+ enum tty_state_t tty_state; /* TTY status */
+ struct iucv_path *path; /* IUCV path pointer */
+ spinlock_t lock; /* hvc_iucv_private lock */
+ struct list_head tty_outqueue; /* outgoing IUCV messages */
+ struct list_head tty_inqueue; /* incoming IUCV messages */
+};
+
+struct iucv_tty_buffer {
+ struct list_head list; /* list pointer */
+ struct iucv_message msg; /* store an incoming IUCV message */
+ size_t offset; /* data buffer offset */
+ struct iucv_tty_msg *mbuf; /* buffer to store input/output data */
+};
+
+/* IUCV callback handler */
+static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]);
+static void hvc_iucv_path_severed(struct iucv_path *, u8[16]);
+static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *);
+static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *);
+
+
+/* Kernel module parameters */
+static unsigned long hvc_iucv_devices;
+
+/* Array of allocated hvc iucv tty lines... */
+static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES];
+
+/* Kmem cache and mempool for iucv_tty_buffer elements */
+static struct kmem_cache *hvc_iucv_buffer_cache;
+static mempool_t *hvc_iucv_mempool;
+
+/* IUCV handler callback functions */
+static struct iucv_handler hvc_iucv_handler = {
+ .path_pending = hvc_iucv_path_pending,
+ .path_severed = hvc_iucv_path_severed,
+ .message_complete = hvc_iucv_msg_complete,
+ .message_pending = hvc_iucv_msg_pending,
+};
+
+
+/**
+ * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance.
+ * @num: The HVC virtual terminal number (vtermno)
+ *
+ * This function returns the struct hvc_iucv_private instance that corresponds
+ * to the HVC virtual terminal number specified as parameter @num.
+ */
+struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
+{
+ if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices))
+ return NULL;
+ return hvc_iucv_table[num - HVC_IUCV_MAGIC];
+}
+
+/**
+ * alloc_tty_buffer() - Returns a new struct iucv_tty_buffer element.
+ * @size: Size of the internal buffer used to store data.
+ * @flags: Memory allocation flags passed to mempool.
+ *
+ * This function allocates a new struct iucv_tty_buffer element and, optionally,
+ * allocates an internal data buffer with the specified size @size.
+ * Note: The total message size arises from the internal buffer size and the
+ * members of the iucv_tty_msg structure.
+ *
+ * The function returns NULL if memory allocation has failed.
+ */
+static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags)
+{
+ struct iucv_tty_buffer *bufp;
+
+ bufp = mempool_alloc(hvc_iucv_mempool, flags);
+ if (!bufp)
+ return NULL;
+ memset(bufp, 0, sizeof(struct iucv_tty_buffer));
+
+ if (size > 0) {
+ bufp->msg.length = MSG_SIZE(size);
+ bufp->mbuf = kmalloc(bufp->msg.length, flags);
+ if (!bufp->mbuf) {
+ mempool_free(bufp, hvc_iucv_mempool);
+ return NULL;
+ }
+ bufp->mbuf->version = MSG_VERSION;
+ bufp->mbuf->type = MSG_TYPE_DATA;
+ bufp->mbuf->datalen = (u16) size;
+ }
+ return bufp;
+}
+
+/**
+ * destroy_tty_buffer() - destroy struct iucv_tty_buffer element.
+ * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL.
+ *
+ * The destroy_tty_buffer() function frees the internal data buffer and returns
+ * the struct iucv_tty_buffer element back to the mempool for freeing.
+ */
+static void destroy_tty_buffer(struct iucv_tty_buffer *bufp)
+{
+ kfree(bufp->mbuf);
+ mempool_free(bufp, hvc_iucv_mempool);
+}
+
+/**
+ * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element.
+ * @list: List head pointer to a list containing struct iucv_tty_buffer
+ * elements.
+ *
+ * Calls destroy_tty_buffer() for each struct iucv_tty_buffer element in the
+ * list @list.
+ */
+static void destroy_tty_buffer_list(struct list_head *list)
+{
+ struct iucv_tty_buffer *ent, *next;
+
+ list_for_each_entry_safe(ent, next, list, list) {
+ list_del(&ent->list);
+ destroy_tty_buffer(ent);
+ }
+}
+
+/**
+ * hvc_iucv_write() - Receive IUCV message write data to HVC console buffer.
+ * @priv: Pointer to hvc_iucv_private structure.
+ * @buf: HVC console buffer for writing received terminal data.
+ * @count: HVC console buffer size.
+ * @has_more_data: Pointer to an int variable.
+ *
+ * The function picks up pending messages from the input queue and receives
+ * the message data that is then written to the specified buffer @buf.
+ * If the buffer size @count is less than the data message size, then the
+ * message is kept on the input queue and @has_more_data is set to 1.
+ * If the message data has been entirely written, the message is removed from
+ * the input queue.
+ *
+ * The function returns the number of bytes written to the terminal, zero if
+ * there are no pending data messages available or if there is no established
+ * IUCV path.
+ * If the IUCV path has been severed, then -EPIPE is returned to cause a
+ * hang up (that is issued by the HVC console layer).
+ */
+static int hvc_iucv_write(struct hvc_iucv_private *priv,
+ char *buf, int count, int *has_more_data)
+{
+ struct iucv_tty_buffer *rb;
+ int written;
+ int rc;
+
+ /* Immediately return if there is no IUCV connection */
+ if (priv->iucv_state == IUCV_DISCONN)
+ return 0;
+
+ /* If the IUCV path has been severed, return -EPIPE to inform the
+ * hvc console layer to hang up the tty device. */
+ if (priv->iucv_state == IUCV_SEVERED)
+ return -EPIPE;
+
+ /* check if there are pending messages */
+ if (list_empty(&priv->tty_inqueue))
+ return 0;
+
+ /* receive a iucv message and flip data to the tty (ldisc) */
+ rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list);
+
+ written = 0;
+ if (!rb->mbuf) { /* message not yet received ... */
+ /* allocate mem to store msg data; if no memory is available
+ * then leave the buffer on the list and re-try later */
+ rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC);
+ if (!rb->mbuf)
+ return -ENOMEM;
+
+ rc = __iucv_message_receive(priv->path, &rb->msg, 0,
+ rb->mbuf, rb->msg.length, NULL);
+ switch (rc) {
+ case 0: /* Successful */
+ break;
+ case 2: /* No message found */
+ case 9: /* Message purged */
+ break;
+ default:
+ written = -EIO;
+ }
+ /* remove buffer if an error has occured or received data
+ * is not correct */
+ if (rc || (rb->mbuf->version != MSG_VERSION) ||
+ (rb->msg.length != MSG_SIZE(rb->mbuf->datalen)))
+ goto out_remove_buffer;
+ }
+
+ switch (rb->mbuf->type) {
+ case MSG_TYPE_DATA:
+ written = min_t(int, rb->mbuf->datalen - rb->offset, count);
+ memcpy(buf, rb->mbuf->data + rb->offset, written);
+ if (written < (rb->mbuf->datalen - rb->offset)) {
+ rb->offset += written;
+ *has_more_data = 1;
+ goto out_written;
+ }
+ break;
+
+ case MSG_TYPE_WINSIZE:
+ if (rb->mbuf->datalen != sizeof(struct winsize))
+ break;
+ hvc_resize(priv->hvc, *((struct winsize *)rb->mbuf->data));
+ break;
+
+ case MSG_TYPE_ERROR: /* ignored ... */
+ case MSG_TYPE_TERMENV: /* ignored ... */
+ case MSG_TYPE_TERMIOS: /* ignored ... */
+ break;
+ }
+
+out_remove_buffer:
+ list_del(&rb->list);
+ destroy_tty_buffer(rb);
+ *has_more_data = !list_empty(&priv->tty_inqueue);
+
+out_written:
+ return written;
+}
+
+/**
+ * hvc_iucv_get_chars() - HVC get_chars operation.
+ * @vtermno: HVC virtual terminal number.
+ * @buf: Pointer to a buffer to store data
+ * @count: Size of buffer available for writing
+ *
+ * The hvc_console thread calls this method to read characters from
+ * the terminal backend. If an IUCV communication path has been established,
+ * pending IUCV messages are received and data is copied into buffer @buf
+ * up to @count bytes.
+ *
+ * Locking: The routine gets called under an irqsave() spinlock; and
+ * the routine locks the struct hvc_iucv_private->lock to call
+ * helper functions.
+ */
+static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count)
+{
+ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
+ int written;
+ int has_more_data;
+
+ if (count <= 0)
+ return 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+ has_more_data = 0;
+ written = hvc_iucv_write(priv, buf, count, &has_more_data);
+ spin_unlock(&priv->lock);
+
+ /* if there are still messages on the queue... schedule another run */
+ if (has_more_data)
+ hvc_kick();
+
+ return written;
+}
+
+/**
+ * hvc_iucv_send() - Send an IUCV message containing terminal data.
+ * @priv: Pointer to struct hvc_iucv_private instance.
+ * @buf: Buffer containing data to send.
+ * @size: Size of buffer and amount of data to send.
+ *
+ * If an IUCV communication path is established, the function copies the buffer
+ * data to a newly allocated struct iucv_tty_buffer element, sends the data and
+ * puts the element to the outqueue.
+ *
+ * If there is no IUCV communication path established, the function returns 0.
+ * If an existing IUCV communicaton path has been severed, the function returns
+ * -EPIPE (can be passed to HVC layer to cause a tty hangup).
+ */
+static int hvc_iucv_send(struct hvc_iucv_private *priv, const char *buf,
+ int count)
+{
+ struct iucv_tty_buffer *sb;
+ int rc;
+ u16 len;
+
+ if (priv->iucv_state == IUCV_SEVERED)
+ return -EPIPE;
+
+ if (priv->iucv_state == IUCV_DISCONN)
+ return 0;
+
+ len = min_t(u16, MSG_MAX_DATALEN, count);
+
+ /* allocate internal buffer to store msg data and also compute total
+ * message length */
+ sb = alloc_tty_buffer(len, GFP_ATOMIC);
+ if (!sb)
+ return -ENOMEM;
+
+ sb->mbuf->datalen = len;
+ memcpy(sb->mbuf->data, buf, len);
+
+ list_add_tail(&sb->list, &priv->tty_outqueue);
+
+ rc = __iucv_message_send(priv->path, &sb->msg, 0, 0,
+ (void *) sb->mbuf, sb->msg.length);
+ if (rc) {
+ list_del(&sb->list);
+ destroy_tty_buffer(sb);
+ len = 0;
+ }
+
+ return len;
+}
+
+/**
+ * hvc_iucv_put_chars() - HVC put_chars operation.
+ * @vtermno: HVC virtual terminal number.
+ * @buf: Pointer to an buffer to read data from
+ * @count: Size of buffer available for reading
+ *
+ * The hvc_console thread calls this method to write characters from
+ * to the terminal backend.
+ * The function calls hvc_iucv_send() under the lock of the
+ * struct hvc_iucv_private instance that corresponds to the tty @vtermno.
+ *
+ * Locking: The method gets called under an irqsave() spinlock; and
+ * locks struct hvc_iucv_private->lock.
+ */
+static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count)
+{
+ struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno);
+ int sent;
+
+ if (count <= 0)
+ return 0;
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+ sent = hvc_iucv_send(priv, buf, count);
+ spin_unlock(&priv->lock);
+
+ return sent;
+}
+
+/**
+ * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc): the index of an struct
+ * hvc_iucv_private instance.
+ *
+ * The function sets the tty state to TTY_OPEN for the struct hvc_iucv_private
+ * instance that is derived from @id. Always returns 0.
+ *
+ * Locking: struct hvc_iucv_private->lock, spin_lock_bh
+ */
+static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return 0;
+
+ spin_lock_bh(&priv->lock);
+ priv->tty_state = TTY_OPENED;
+ spin_unlock_bh(&priv->lock);
+
+ return 0;
+}
+
+/**
+ * hvc_iucv_cleanup() - Clean up function if the tty portion is finally closed.
+ * @priv: Pointer to the struct hvc_iucv_private instance.
+ *
+ * The functions severs the established IUCV communication path (if any), and
+ * destroy struct iucv_tty_buffer elements from the in- and outqueue. Finally,
+ * the functions resets the states to TTY_CLOSED and IUCV_DISCONN.
+ */
+static void hvc_iucv_cleanup(struct hvc_iucv_private *priv)
+{
+ destroy_tty_buffer_list(&priv->tty_outqueue);
+ destroy_tty_buffer_list(&priv->tty_inqueue);
+
+ priv->tty_state = TTY_CLOSED;
+ priv->iucv_state = IUCV_DISCONN;
+}
+
+/**
+ * hvc_iucv_notifier_hangup() - HVC notifier for tty hangups.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc): the index of an struct
+ * hvc_iucv_private instance.
+ *
+ * This routine notifies the HVC backend that a tty hangup (carrier loss,
+ * virtual or otherwise) has occured.
+ *
+ * The HVC backend for z/VM IUCV ignores virtual hangups (vhangup()), to keep
+ * an existing IUCV communication path established.
+ * (Background: vhangup() is called from user space (by getty or login) to
+ * disable writing to the tty by other applications).
+ *
+ * If the tty has been opened (e.g. getty) and an established IUCV path has been
+ * severed (we caused the tty hangup in that case), then the functions invokes
+ * hvc_iucv_cleanup() to clean up.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return;
+
+ spin_lock_bh(&priv->lock);
+ /* NOTE: If the hangup was scheduled by ourself (from the iucv
+ * path_servered callback [IUCV_SEVERED]), then we have to
+ * finally clean up the tty backend structure and set state to
+ * TTY_CLOSED.
+ *
+ * If the tty was hung up otherwise (e.g. vhangup()), then we
+ * ignore this hangup and keep an established IUCV path open...
+ * (...the reason is that we are not able to connect back to the
+ * client if we disconnect on hang up) */
+ priv->tty_state = TTY_CLOSED;
+
+ if (priv->iucv_state == IUCV_SEVERED)
+ hvc_iucv_cleanup(priv);
+ spin_unlock_bh(&priv->lock);
+}
+
+/**
+ * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time.
+ * @hp: Pointer to the HVC device (struct hvc_struct)
+ * @id: Additional data (originally passed to hvc_alloc):
+ * the index of an struct hvc_iucv_private instance.
+ *
+ * This routine notifies the HVC backend that the last tty device file
+ * descriptor has been closed.
+ * The function calls hvc_iucv_cleanup() to clean up the struct hvc_iucv_private
+ * instance.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
+{
+ struct hvc_iucv_private *priv;
+ struct iucv_path *path;
+
+ priv = hvc_iucv_get_private(id);
+ if (!priv)
+ return;
+
+ spin_lock_bh(&priv->lock);
+ path = priv->path; /* save reference to IUCV path */
+ priv->path = NULL;
+ hvc_iucv_cleanup(priv);
+ spin_unlock_bh(&priv->lock);
+
+ /* sever IUCV path outside of priv->lock due to lock ordering of:
+ * priv->lock <--> iucv_table_lock */
+ if (path) {
+ iucv_path_sever(path, NULL);
+ iucv_path_free(path);
+ }
+}
+
+/**
+ * hvc_iucv_path_pending() - IUCV handler to process a connection request.
+ * @path: Pending path (struct iucv_path)
+ * @ipvmid: Originator z/VM system identifier
+ * @ipuser: User specified data for this path
+ * (AF_IUCV: port/service name and originator port)
+ *
+ * The function uses the @ipuser data to check to determine if the pending
+ * path belongs to a terminal managed by this HVC backend.
+ * If the check is successful, then an additional check is done to ensure
+ * that a terminal cannot be accessed multiple times (only one connection
+ * to a terminal is allowed). In that particular case, the pending path is
+ * severed. If it is the first connection, the pending path is accepted and
+ * associated to the struct hvc_iucv_private. The iucv state is updated to
+ * reflect that a communication path has been established.
+ *
+ * Returns 0 if the path belongs to a terminal managed by the this HVC backend;
+ * otherwise returns -ENODEV in order to dispatch this path to other handlers.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static int hvc_iucv_path_pending(struct iucv_path *path,
+ u8 ipvmid[8], u8 ipuser[16])
+{
+ struct hvc_iucv_private *priv;
+ u8 nuser_data[16];
+ int i, rc;
+
+ priv = NULL;
+ for (i = 0; i < hvc_iucv_devices; i++)
+ if (hvc_iucv_table[i] &&
+ (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) {
+ priv = hvc_iucv_table[i];
+ break;
+ }
+
+ if (!priv)
+ return -ENODEV;
+
+ spin_lock(&priv->lock);
+
+ /* If the terminal is already connected or being severed, then sever
+ * this path to enforce that there is only ONE established communication
+ * path per terminal. */
+ if (priv->iucv_state != IUCV_DISCONN) {
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+ goto out_path_handled;
+ }
+
+ /* accept path */
+ memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */
+ memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */
+ path->msglim = 0xffff; /* IUCV MSGLIMIT */
+ path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */
+ rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv);
+ if (rc) {
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+ goto out_path_handled;
+ }
+ priv->path = path;
+ priv->iucv_state = IUCV_CONNECTED;
+
+out_path_handled:
+ spin_unlock(&priv->lock);
+ return 0;
+}
+
+/**
+ * hvc_iucv_path_severed() - IUCV handler to process a path sever.
+ * @path: Pending path (struct iucv_path)
+ * @ipuser: User specified data for this path
+ * (AF_IUCV: port/service name and originator port)
+ *
+ * The function also severs the path (as required by the IUCV protocol) and
+ * sets the iucv state to IUCV_SEVERED for the associated struct
+ * hvc_iucv_private instance. Later, the IUCV_SEVERED state triggers a tty
+ * hangup (hvc_iucv_get_chars() / hvc_iucv_write()).
+ *
+ * If tty portion of the HVC is closed then clean up the outqueue in addition.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
+{
+ struct hvc_iucv_private *priv = path->private;
+
+ spin_lock(&priv->lock);
+ priv->iucv_state = IUCV_SEVERED;
+
+ /* NOTE: If the tty has not yet been opened by a getty program
+ * (e.g. to see console messages), then cleanup the
+ * hvc_iucv_private structure to allow re-connects.
+ *
+ * If the tty has been opened, the get_chars() callback returns
+ * -EPIPE to signal the hvc console layer to hang up the tty. */
+ priv->path = NULL;
+ if (priv->tty_state == TTY_CLOSED)
+ hvc_iucv_cleanup(priv);
+ spin_unlock(&priv->lock);
+
+ /* finally sever path (outside of priv->lock due to lock ordering) */
+ iucv_path_sever(path, ipuser);
+ iucv_path_free(path);
+}
+
+/**
+ * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message.
+ * @path: Pending path (struct iucv_path)
+ * @msg: Pointer to the IUCV message
+ *
+ * The function stores an incoming message on the input queue for later
+ * processing (by hvc_iucv_get_chars() / hvc_iucv_write()).
+ * However, if the tty has not yet been opened, the message is rejected.
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_msg_pending(struct iucv_path *path,
+ struct iucv_message *msg)
+{
+ struct hvc_iucv_private *priv = path->private;
+ struct iucv_tty_buffer *rb;
+
+ spin_lock(&priv->lock);
+
+ /* reject messages if tty has not yet been opened */
+ if (priv->tty_state == TTY_CLOSED) {
+ iucv_message_reject(path, msg);
+ goto unlock_return;
+ }
+
+ /* allocate buffer an empty buffer element */
+ rb = alloc_tty_buffer(0, GFP_ATOMIC);
+ if (!rb) {
+ iucv_message_reject(path, msg);
+ goto unlock_return; /* -ENOMEM */
+ }
+ rb->msg = *msg;
+
+ list_add_tail(&rb->list, &priv->tty_inqueue);
+
+ hvc_kick(); /* wakup hvc console thread */
+
+unlock_return:
+ spin_unlock(&priv->lock);
+}
+
+/**
+ * hvc_iucv_msg_complete() - IUCV handler to process message completion
+ * @path: Pending path (struct iucv_path)
+ * @msg: Pointer to the IUCV message
+ *
+ * The function is called upon completion of message delivery and the
+ * message is removed from the outqueue. Additional delivery information
+ * can be found in msg->audit: rejected messages (0x040000 (IPADRJCT)) and
+ * purged messages (0x010000 (IPADPGNR)).
+ *
+ * Locking: struct hvc_iucv_private->lock
+ */
+static void hvc_iucv_msg_complete(struct iucv_path *path,
+ struct iucv_message *msg)
+{
+ struct hvc_iucv_private *priv = path->private;
+ struct iucv_tty_buffer *ent, *next;
+ LIST_HEAD(list_remove);
+
+ spin_lock(&priv->lock);
+ list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list)
+ if (ent->msg.id == msg->id) {
+ list_move(&ent->list, &list_remove);
+ break;
+ }
+ spin_unlock(&priv->lock);
+ destroy_tty_buffer_list(&list_remove);
+}
+
+
+/* HVC operations */
+static struct hv_ops hvc_iucv_ops = {
+ .get_chars = hvc_iucv_get_chars,
+ .put_chars = hvc_iucv_put_chars,
+ .notifier_add = hvc_iucv_notifier_add,
+ .notifier_del = hvc_iucv_notifier_del,
+ .notifier_hangup = hvc_iucv_notifier_hangup,
+};
+
+/**
+ * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance
+ * @id: hvc_iucv_table index
+ *
+ * This function allocates a new hvc_iucv_private struct and put the
+ * instance into hvc_iucv_table at index @id.
+ * Returns 0 on success; otherwise non-zero.
+ */
+static int __init hvc_iucv_alloc(int id)
+{
+ struct hvc_iucv_private *priv;
+ char name[9];
+ int rc;
+
+ priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->tty_outqueue);
+ INIT_LIST_HEAD(&priv->tty_inqueue);
+
+ /* Finally allocate hvc */
+ priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id,
+ HVC_IUCV_MAGIC + id, &hvc_iucv_ops, PAGE_SIZE);
+ if (IS_ERR(priv->hvc)) {
+ rc = PTR_ERR(priv->hvc);
+ kfree(priv);
+ return rc;
+ }
+
+ /* setup iucv related information */
+ snprintf(name, 9, "ihvc%-4d", id);
+ memcpy(priv->srv_name, name, 8);
+ ASCEBC(priv->srv_name, 8);
+
+ hvc_iucv_table[id] = priv;
+ return 0;
+}
+
+/**
+ * hvc_iucv_init() - Initialization of HVC backend for z/VM IUCV
+ */
+static int __init hvc_iucv_init(void)
+{
+ int rc, i;
+
+ if (!MACHINE_IS_VM) {
+ pr_warning("The z/VM IUCV Hypervisor console cannot be "
+ "used without z/VM.\n");
+ return -ENODEV;
+ }
+
+ if (!hvc_iucv_devices)
+ return -ENODEV;
+
+ if (hvc_iucv_devices > MAX_HVC_IUCV_LINES)
+ return -EINVAL;
+
+ hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT,
+ sizeof(struct iucv_tty_buffer),
+ 0, 0, NULL);
+ if (!hvc_iucv_buffer_cache) {
+ pr_err("Not enough memory for driver initialization "
+ "(rs=%d).\n", 1);
+ return -ENOMEM;
+ }
+
+ hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR,
+ hvc_iucv_buffer_cache);
+ if (!hvc_iucv_mempool) {
+ pr_err("Not enough memory for driver initialization "
+ "(rs=%d).\n", 2);
+ kmem_cache_destroy(hvc_iucv_buffer_cache);
+ return -ENOMEM;
+ }
+
+ /* allocate hvc_iucv_private structs */
+ for (i = 0; i < hvc_iucv_devices; i++) {
+ rc = hvc_iucv_alloc(i);
+ if (rc) {
+ pr_err("Could not create new z/VM IUCV HVC backend "
+ "rc=%d.\n", rc);
+ goto out_error_hvc;
+ }
+ }
+
+ /* register IUCV callback handler */
+ rc = iucv_register(&hvc_iucv_handler, 0);
+ if (rc) {
+ pr_err("Could not register iucv handler (rc=%d).\n", rc);
+ goto out_error_iucv;
+ }
+
+ return 0;
+
+out_error_iucv:
+ iucv_unregister(&hvc_iucv_handler, 0);
+out_error_hvc:
+ for (i = 0; i < hvc_iucv_devices; i++)
+ if (hvc_iucv_table[i]) {
+ if (hvc_iucv_table[i]->hvc)
+ hvc_remove(hvc_iucv_table[i]->hvc);
+ kfree(hvc_iucv_table[i]);
+ }
+ mempool_destroy(hvc_iucv_mempool);
+ kmem_cache_destroy(hvc_iucv_buffer_cache);
+ return rc;
+}
+
+/**
+ * hvc_iucv_console_init() - Early console initialization
+ */
+static int __init hvc_iucv_console_init(void)
+{
+ if (!MACHINE_IS_VM || !hvc_iucv_devices)
+ return -ENODEV;
+ return hvc_instantiate(HVC_IUCV_MAGIC, 0, &hvc_iucv_ops);
+}
+
+/**
+ * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter
+ * @val: Parameter value (numeric)
+ */
+static int __init hvc_iucv_config(char *val)
+{
+ return strict_strtoul(val, 10, &hvc_iucv_devices);
+}
+
+
+module_init(hvc_iucv_init);
+console_initcall(hvc_iucv_console_init);
+__setup("hvc_iucv=", hvc_iucv_config);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HVC back-end for z/VM IUCV.");
+MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 363bd1303d2..570ae59c1d5 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1898,15 +1898,19 @@ restart_cb:
wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED));
/* Process finished ERP request. */
if (cqr->refers) {
+ spin_lock_bh(&block->queue_lock);
__dasd_block_process_erp(block, cqr);
+ spin_unlock_bh(&block->queue_lock);
/* restart list_for_xx loop since dasd_process_erp
* might remove multiple elements */
goto restart_cb;
}
/* call the callback function */
+ spin_lock_irq(&block->request_queue_lock);
cqr->endclk = get_clock();
list_del_init(&cqr->blocklist);
__dasd_cleanup_cqr(cqr);
+ spin_unlock_irq(&block->request_queue_lock);
}
return rc;
}
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 921443b01d1..2ef25731d19 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -23,6 +23,7 @@
/* This is ugly... */
#define PRINTK_HEADER "dasd_devmap:"
+#define DASD_BUS_ID_SIZE 20
#include "dasd_int.h"
@@ -41,7 +42,7 @@ EXPORT_SYMBOL_GPL(dasd_page_cache);
*/
struct dasd_devmap {
struct list_head list;
- char bus_id[BUS_ID_SIZE];
+ char bus_id[DASD_BUS_ID_SIZE];
unsigned int devindex;
unsigned short features;
struct dasd_device *device;
@@ -94,7 +95,7 @@ dasd_hash_busid(const char *bus_id)
int hash, i;
hash = 0;
- for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
+ for (i = 0; (i < DASD_BUS_ID_SIZE) && *bus_id; i++, bus_id++)
hash += *bus_id;
return hash & 0xff;
}
@@ -301,7 +302,7 @@ dasd_parse_range( char *parsestring ) {
int from, from_id0, from_id1;
int to, to_id0, to_id1;
int features, rc;
- char bus_id[BUS_ID_SIZE+1], *str;
+ char bus_id[DASD_BUS_ID_SIZE+1], *str;
str = parsestring;
rc = dasd_busid(&str, &from_id0, &from_id1, &from);
@@ -407,14 +408,14 @@ dasd_add_busid(const char *bus_id, int features)
devmap = NULL;
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list)
- if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+ if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
devmap = tmp;
break;
}
if (!devmap) {
/* This bus_id is new. */
new->devindex = dasd_max_devindex++;
- strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
+ strncpy(new->bus_id, bus_id, DASD_BUS_ID_SIZE);
new->features = features;
new->device = NULL;
list_add(&new->list, &dasd_hashlists[hash]);
@@ -439,7 +440,7 @@ dasd_find_busid(const char *bus_id)
devmap = ERR_PTR(-ENODEV);
hash = dasd_hash_busid(bus_id);
list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
- if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+ if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
devmap = tmp;
break;
}
@@ -561,7 +562,7 @@ dasd_create_device(struct ccw_device *cdev)
}
spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
- cdev->dev.driver_data = device;
+ dev_set_drvdata(&cdev->dev, device);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
return device;
@@ -597,7 +598,7 @@ dasd_delete_device(struct dasd_device *device)
/* Disconnect dasd_device structure from ccw_device structure. */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->cdev->dev.driver_data = NULL;
+ dev_set_drvdata(&device->cdev->dev, NULL);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
/*
@@ -638,7 +639,7 @@ dasd_put_device_wake(struct dasd_device *device)
struct dasd_device *
dasd_device_from_cdev_locked(struct ccw_device *cdev)
{
- struct dasd_device *device = cdev->dev.driver_data;
+ struct dasd_device *device = dev_get_drvdata(&cdev->dev);
if (!device)
return ERR_PTR(-ENODEV);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 2e60d5f968c..bd2c52e2076 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1496,7 +1496,7 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
/* service information message SIM */
- if (irb->esw.esw0.erw.cons && (irb->ecw[27] & DASD_SENSE_BIT_0) &&
+ if (irb->esw.esw0.erw.cons && !(irb->ecw[27] & DASD_SENSE_BIT_0) &&
((irb->ecw[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) {
dasd_3990_erp_handle_sim(device, irb->ecw);
dasd_schedule_device_bh(device);
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 9088de84b45..bf6fd348f20 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -180,12 +180,12 @@ dasd_calc_metrics(char *page, char **start, off_t off,
#ifdef CONFIG_DASD_PROFILE
static char *
-dasd_statistics_array(char *str, unsigned int *array, int shift)
+dasd_statistics_array(char *str, unsigned int *array, int factor)
{
int i;
for (i = 0; i < 32; i++) {
- str += sprintf(str, "%7d ", array[i] >> shift);
+ str += sprintf(str, "%7d ", array[i] / factor);
if (i == 15)
str += sprintf(str, "\n");
}
@@ -202,7 +202,7 @@ dasd_statistics_read(char *page, char **start, off_t off,
#ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t *prof;
char *str;
- int shift;
+ int factor;
/* check for active profiling */
if (dasd_profile_level == DASD_PROFILE_OFF) {
@@ -214,12 +214,14 @@ dasd_statistics_read(char *page, char **start, off_t off,
prof = &dasd_global_profile;
/* prevent couter 'overflow' on output */
- for (shift = 0; (prof->dasd_io_reqs >> shift) > 9999999; shift++);
+ for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
+ factor *= 10);
str = page;
str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs);
- str += sprintf(str, "with %d sectors(512B each)\n",
+ str += sprintf(str, "with %u sectors(512B each)\n",
prof->dasd_io_sects);
+ str += sprintf(str, "Scale Factor is %d\n", factor);
str += sprintf(str,
" __<4 ___8 __16 __32 __64 _128 "
" _256 _512 __1k __2k __4k __8k "
@@ -230,22 +232,22 @@ dasd_statistics_read(char *page, char **start, off_t off,
" __1G __2G __4G " " _>4G\n");
str += sprintf(str, "Histogram of sizes (512B secs)\n");
- str = dasd_statistics_array(str, prof->dasd_io_secs, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_secs, factor);
str += sprintf(str, "Histogram of I/O times (microseconds)\n");
- str = dasd_statistics_array(str, prof->dasd_io_times, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_times, factor);
str += sprintf(str, "Histogram of I/O times per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_timps, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_timps, factor);
str += sprintf(str, "Histogram of I/O time till ssch\n");
- str = dasd_statistics_array(str, prof->dasd_io_time1, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time1, factor);
str += sprintf(str, "Histogram of I/O time between ssch and irq\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time2, factor);
str += sprintf(str, "Histogram of I/O time between ssch "
"and irq per sector\n");
- str = dasd_statistics_array(str, prof->dasd_io_time2ps, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time2ps, factor);
str += sprintf(str, "Histogram of I/O time between irq and end\n");
- str = dasd_statistics_array(str, prof->dasd_io_time3, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_time3, factor);
str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n");
- str = dasd_statistics_array(str, prof->dasd_io_nr_req, shift);
+ str = dasd_statistics_array(str, prof->dasd_io_nr_req, factor);
len = str - page;
#else
len = sprintf(page, "Statistics are not activated in this kernel\n");
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 63f26a135fe..26ffc6ab441 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -4,6 +4,9 @@
* Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer
*/
+#define KMSG_COMPONENT "dcssblk"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h>
@@ -17,19 +20,10 @@
#include <linux/interrupt.h>
#include <asm/s390_rdev.h>
-//#define DCSSBLK_DEBUG /* Debug messages on/off */
#define DCSSBLK_NAME "dcssblk"
#define DCSSBLK_MINORS_PER_DISK 1
#define DCSSBLK_PARM_LEN 400
-
-#ifdef DCSSBLK_DEBUG
-#define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x)
-#else
-#define PRINT_DEBUG(x...) do {} while (0)
-#endif
-#define PRINT_INFO(x...) printk(KERN_INFO DCSSBLK_NAME " info: " x)
-#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
-#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
+#define DCSS_BUS_ID_SIZE 20
static int dcssblk_open(struct block_device *bdev, fmode_t mode);
static int dcssblk_release(struct gendisk *disk, fmode_t mode);
@@ -50,7 +44,7 @@ static struct block_device_operations dcssblk_devops = {
struct dcssblk_dev_info {
struct list_head lh;
struct device dev;
- char segment_name[BUS_ID_SIZE];
+ char segment_name[DCSS_BUS_ID_SIZE];
atomic_t use_count;
struct gendisk *gd;
unsigned long start;
@@ -65,7 +59,7 @@ struct dcssblk_dev_info {
struct segment_info {
struct list_head lh;
- char segment_name[BUS_ID_SIZE];
+ char segment_name[DCSS_BUS_ID_SIZE];
unsigned long start;
unsigned long end;
int segment_type;
@@ -261,10 +255,9 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
/* check continuity */
for (i = 0; i < dev_info->num_of_segments - 1; i++) {
if ((sort_list[i].end + 1) != sort_list[i+1].start) {
- PRINT_ERR("Segment %s is not contiguous with "
- "segment %s\n",
- sort_list[i].segment_name,
- sort_list[i+1].segment_name);
+ pr_err("Adjacent DCSSs %s and %s are not "
+ "contiguous\n", sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
rc = -EINVAL;
goto out;
}
@@ -275,10 +268,10 @@ dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
!(sort_list[i+1].segment_type &
SEGMENT_EXCLUSIVE) ||
(sort_list[i+1].segment_type == SEG_TYPE_ER)) {
- PRINT_ERR("Segment %s has different type from "
- "segment %s\n",
- sort_list[i].segment_name,
- sort_list[i+1].segment_name);
+ pr_err("DCSS %s and DCSS %s have "
+ "incompatible types\n",
+ sort_list[i].segment_name,
+ sort_list[i+1].segment_name);
rc = -EINVAL;
goto out;
}
@@ -380,8 +373,9 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
} else if (inbuf[0] == '0') {
/* reload segments in exclusive mode */
if (dev_info->segment_type == SEG_TYPE_SC) {
- PRINT_ERR("Segment type SC (%s) cannot be loaded in "
- "non-shared mode\n", dev_info->segment_name);
+ pr_err("DCSS %s is of type SC and cannot be "
+ "loaded as exclusive-writable\n",
+ dev_info->segment_name);
rc = -EINVAL;
goto out;
}
@@ -404,9 +398,8 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
goto out;
removeseg:
- PRINT_ERR("Could not reload segment(s) of the device %s, removing "
- "segment(s) now!\n",
- dev_info->segment_name);
+ pr_err("DCSS device %s is removed after a failed access mode "
+ "change\n", dev_info->segment_name);
temp = entry;
list_for_each_entry(entry, &dev_info->seg_list, lh) {
if (entry != temp)
@@ -454,17 +447,17 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
if (inbuf[0] == '1') {
if (atomic_read(&dev_info->use_count) == 0) {
// device is idle => we save immediately
- PRINT_INFO("Saving segment(s) of the device %s\n",
- dev_info->segment_name);
+ pr_info("All DCSSs that map to device %s are "
+ "saved\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) {
segment_save(entry->segment_name);
}
} else {
// device is busy => we save it when it becomes
// idle in dcssblk_release
- PRINT_INFO("Device %s is currently busy, segment(s) "
- "will be saved when it becomes idle...\n",
- dev_info->segment_name);
+ pr_info("Device %s is in use, its DCSSs will be "
+ "saved when it becomes idle\n",
+ dev_info->segment_name);
dev_info->save_pending = 1;
}
} else if (inbuf[0] == '0') {
@@ -472,9 +465,9 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
// device is busy & the user wants to undo his save
// request
dev_info->save_pending = 0;
- PRINT_INFO("Pending save for segment(s) of the device "
- "%s deactivated\n",
- dev_info->segment_name);
+ pr_info("A pending save request for device %s "
+ "has been canceled\n",
+ dev_info->segment_name);
}
} else {
up_write(&dcssblk_devices_sem);
@@ -614,9 +607,8 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
- PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
- "capacity = %lu (512 Byte) sectors\n", local_buf,
- seg_byte_size, seg_byte_size >> 9);
+ pr_info("Loaded %s with total size %lu bytes and capacity %lu "
+ "sectors\n", local_buf, seg_byte_size, seg_byte_size >> 9);
dev_info->save_pending = 0;
dev_info->is_shared = 1;
@@ -744,13 +736,15 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
dev_info = dcssblk_get_device_by_name(local_buf);
if (dev_info == NULL) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Device %s is not loaded!\n", local_buf);
+ pr_warning("Device %s cannot be removed because it is not a "
+ "known device\n", local_buf);
rc = -ENODEV;
goto out_buf;
}
if (atomic_read(&dev_info->use_count) != 0) {
up_write(&dcssblk_devices_sem);
- PRINT_WARN("Device %s is in use!\n", local_buf);
+ pr_warning("Device %s cannot be removed while it is in "
+ "use\n", local_buf);
rc = -EBUSY;
goto out_buf;
}
@@ -807,8 +801,8 @@ dcssblk_release(struct gendisk *disk, fmode_t mode)
down_write(&dcssblk_devices_sem);
if (atomic_dec_and_test(&dev_info->use_count)
&& (dev_info->save_pending)) {
- PRINT_INFO("Device %s became idle and is being saved now\n",
- dev_info->segment_name);
+ pr_info("Device %s has become idle and is being saved "
+ "now\n", dev_info->segment_name);
list_for_each_entry(entry, &dev_info->seg_list, lh) {
segment_save(entry->segment_name);
}
@@ -851,7 +845,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
case SEG_TYPE_SC:
/* cannot write to these segments */
if (bio_data_dir(bio) == WRITE) {
- PRINT_WARN("rejecting write to ro device %s\n",
+ pr_warning("Writing to %s failed because it "
+ "is a read-only device\n",
dev_name(&dev_info->dev));
goto fail;
}
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index 03916989ed2..76814f3e898 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -25,6 +25,9 @@
* generic hard disk support to replace ad-hoc partitioning
*/
+#define KMSG_COMPONENT "xpram"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h> /* isdigit, isxdigit */
@@ -42,12 +45,6 @@
#define XPRAM_DEVS 1 /* one partition */
#define XPRAM_MAX_DEVS 32 /* maximal number of devices (partitions) */
-#define PRINT_DEBUG(x...) printk(KERN_DEBUG XPRAM_NAME " debug:" x)
-#define PRINT_INFO(x...) printk(KERN_INFO XPRAM_NAME " info:" x)
-#define PRINT_WARN(x...) printk(KERN_WARNING XPRAM_NAME " warning:" x)
-#define PRINT_ERR(x...) printk(KERN_ERR XPRAM_NAME " error:" x)
-
-
typedef struct {
unsigned int size; /* size of xpram segment in pages */
unsigned int offset; /* start page of xpram segment */
@@ -264,7 +261,7 @@ static int __init xpram_setup_sizes(unsigned long pages)
/* Check number of devices. */
if (devs <= 0 || devs > XPRAM_MAX_DEVS) {
- PRINT_ERR("invalid number %d of devices\n",devs);
+ pr_err("%d is not a valid number of XPRAM devices\n",devs);
return -EINVAL;
}
xpram_devs = devs;
@@ -295,22 +292,22 @@ static int __init xpram_setup_sizes(unsigned long pages)
mem_auto_no++;
}
- PRINT_INFO(" number of devices (partitions): %d \n", xpram_devs);
+ pr_info(" number of devices (partitions): %d \n", xpram_devs);
for (i = 0; i < xpram_devs; i++) {
if (xpram_sizes[i])
- PRINT_INFO(" size of partition %d: %u kB\n",
- i, xpram_sizes[i]);
+ pr_info(" size of partition %d: %u kB\n",
+ i, xpram_sizes[i]);
else
- PRINT_INFO(" size of partition %d to be set "
- "automatically\n",i);
+ pr_info(" size of partition %d to be set "
+ "automatically\n",i);
}
- PRINT_DEBUG(" memory needed (for sized partitions): %lu kB\n",
- mem_needed);
- PRINT_DEBUG(" partitions to be sized automatically: %d\n",
- mem_auto_no);
+ pr_info(" memory needed (for sized partitions): %lu kB\n",
+ mem_needed);
+ pr_info(" partitions to be sized automatically: %d\n",
+ mem_auto_no);
if (mem_needed > pages * 4) {
- PRINT_ERR("Not enough expanded memory available\n");
+ pr_err("Not enough expanded memory available\n");
return -EINVAL;
}
@@ -322,8 +319,8 @@ static int __init xpram_setup_sizes(unsigned long pages)
*/
if (mem_auto_no) {
mem_auto = ((pages - mem_needed / 4) / mem_auto_no) * 4;
- PRINT_INFO(" automatically determined "
- "partition size: %lu kB\n", mem_auto);
+ pr_info(" automatically determined "
+ "partition size: %lu kB\n", mem_auto);
for (i = 0; i < xpram_devs; i++)
if (xpram_sizes[i] == 0)
xpram_sizes[i] = mem_auto;
@@ -405,12 +402,12 @@ static int __init xpram_init(void)
/* Find out size of expanded memory. */
if (xpram_present() != 0) {
- PRINT_WARN("No expanded memory available\n");
+ pr_err("No expanded memory available\n");
return -ENODEV;
}
xpram_pages = xpram_highest_page_index() + 1;
- PRINT_INFO(" %u pages expanded memory found (%lu KB).\n",
- xpram_pages, (unsigned long) xpram_pages*4);
+ pr_info(" %u pages expanded memory found (%lu KB).\n",
+ xpram_pages, (unsigned long) xpram_pages*4);
rc = xpram_setup_sizes(xpram_pages);
if (rc)
return rc;
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 35fd8dfcaaa..97e63cf4694 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -7,6 +7,9 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
+#define KMSG_COMPONENT "monreader"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -24,19 +27,6 @@
#include <asm/ebcdic.h>
#include <asm/extmem.h>
-//#define MON_DEBUG /* Debug messages on/off */
-
-#define MON_NAME "monreader"
-
-#define P_INFO(x...) printk(KERN_INFO MON_NAME " info: " x)
-#define P_ERROR(x...) printk(KERN_ERR MON_NAME " error: " x)
-#define P_WARNING(x...) printk(KERN_WARNING MON_NAME " warning: " x)
-
-#ifdef MON_DEBUG
-#define P_DEBUG(x...) printk(KERN_DEBUG MON_NAME " debug: " x)
-#else
-#define P_DEBUG(x...) do {} while (0)
-#endif
#define MON_COLLECT_SAMPLE 0x80
#define MON_COLLECT_EVENT 0x40
@@ -172,7 +162,7 @@ static int mon_send_reply(struct mon_msg *monmsg,
} else
monmsg->replied_msglim = 1;
if (rc) {
- P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc);
+ pr_err("Reading monitor data failed with rc=%i\n", rc);
return -EIO;
}
return 0;
@@ -251,7 +241,8 @@ static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
{
struct mon_private *monpriv = path->private;
- P_ERROR("IUCV connection severed with rc = 0x%X\n", ipuser[0]);
+ pr_err("z/VM *MONITOR system service disconnected with rc=%i\n",
+ ipuser[0]);
iucv_path_sever(path, NULL);
atomic_set(&monpriv->iucv_severed, 1);
wake_up(&mon_conn_wait_queue);
@@ -266,8 +257,7 @@ static void mon_iucv_message_pending(struct iucv_path *path,
memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
msg, sizeof(*msg));
if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
- P_WARNING("IUCV message pending, message limit (%i) reached\n",
- MON_MSGLIM);
+ pr_warning("The read queue for monitor data is full\n");
monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
}
monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
@@ -311,8 +301,8 @@ static int mon_open(struct inode *inode, struct file *filp)
rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
MON_SERVICE, NULL, user_data_connect, monpriv);
if (rc) {
- P_ERROR("iucv connection to *MONITOR failed with "
- "IPUSER SEVER code = %i\n", rc);
+ pr_err("Connecting to the z/VM *MONITOR system service "
+ "failed with rc=%i\n", rc);
rc = -EIO;
goto out_path;
}
@@ -353,7 +343,8 @@ static int mon_close(struct inode *inode, struct file *filp)
*/
rc = iucv_path_sever(monpriv->path, user_data_sever);
if (rc)
- P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
+ pr_warning("Disconnecting the z/VM *MONITOR system service "
+ "failed with rc=%i\n", rc);
atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0);
@@ -469,7 +460,8 @@ static int __init mon_init(void)
int rc;
if (!MACHINE_IS_VM) {
- P_ERROR("not running under z/VM, driver not loaded\n");
+ pr_err("The z/VM *MONITOR record device driver cannot be "
+ "loaded without z/VM\n");
return -ENODEV;
}
@@ -478,7 +470,8 @@ static int __init mon_init(void)
*/
rc = iucv_register(&monreader_iucv_handler, 1);
if (rc) {
- P_ERROR("failed to register with iucv driver\n");
+ pr_err("The z/VM *MONITOR record device driver failed to "
+ "register with IUCV\n");
return rc;
}
@@ -488,8 +481,8 @@ static int __init mon_init(void)
goto out_iucv;
}
if (rc != SEG_TYPE_SC) {
- P_ERROR("segment %s has unsupported type, should be SC\n",
- mon_dcss_name);
+ pr_err("The specified *MONITOR DCSS %s does not have the "
+ "required type SC\n", mon_dcss_name);
rc = -EINVAL;
goto out_iucv;
}
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 4d71aa8c1a7..c7d7483bab9 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -8,6 +8,9 @@
* Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
*/
+#define KMSG_COMPONENT "monwriter"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -64,9 +67,9 @@ static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
if (rc <= 0)
return rc;
+ pr_err("Writing monitor data failed with rc=%i\n", rc);
if (rc == 5)
return -EPERM;
- printk("DIAG X'DC' error with return code: %i\n", rc);
return -EINVAL;
}
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index ec9c0bcf66e..50639049641 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -6,6 +6,9 @@
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_cmd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/completion.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -16,9 +19,8 @@
#include <linux/memory.h>
#include <asm/chpid.h>
#include <asm/sclp.h>
-#include "sclp.h"
-#define TAG "sclp_cmd: "
+#include "sclp.h"
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
@@ -169,8 +171,8 @@ static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
/* Check response. */
if (request->status != SCLP_REQ_DONE) {
- printk(KERN_WARNING TAG "sync request failed "
- "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status);
+ pr_warning("sync request failed (cmd=0x%08x, "
+ "status=0x%02x)\n", cmd, request->status);
rc = -EIO;
}
out:
@@ -224,8 +226,8 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info)
if (rc)
goto out;
if (sccb->header.response_code != 0x0010) {
- printk(KERN_WARNING TAG "readcpuinfo failed "
- "(response=0x%04x)\n", sccb->header.response_code);
+ pr_warning("readcpuinfo failed (response=0x%04x)\n",
+ sccb->header.response_code);
rc = -EIO;
goto out;
}
@@ -262,8 +264,9 @@ static int do_cpu_configure(sclp_cmdw_t cmd)
case 0x0120:
break;
default:
- printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, "
- "response=0x%04x)\n", cmd, sccb->header.response_code);
+ pr_warning("configure cpu failed (cmd=0x%08x, "
+ "response=0x%04x)\n", cmd,
+ sccb->header.response_code);
rc = -EIO;
break;
}
@@ -626,9 +629,9 @@ static int do_chp_configure(sclp_cmdw_t cmd)
case 0x0450:
break;
default:
- printk(KERN_WARNING TAG "configure channel-path failed "
- "(cmd=0x%08x, response=0x%04x)\n", cmd,
- sccb->header.response_code);
+ pr_warning("configure channel-path failed "
+ "(cmd=0x%08x, response=0x%04x)\n", cmd,
+ sccb->header.response_code);
rc = -EIO;
break;
}
@@ -695,8 +698,8 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
if (rc)
goto out;
if (sccb->header.response_code != 0x0010) {
- printk(KERN_WARNING TAG "read channel-path info failed "
- "(response=0x%04x)\n", sccb->header.response_code);
+ pr_warning("read channel-path info failed "
+ "(response=0x%04x)\n", sccb->header.response_code);
rc = -EIO;
goto out;
}
diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c
index 4cebd6ee6d2..b497afe061c 100644
--- a/drivers/s390/char/sclp_config.c
+++ b/drivers/s390/char/sclp_config.c
@@ -5,15 +5,17 @@
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_config"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/cpu.h>
#include <linux/sysdev.h>
#include <linux/workqueue.h>
#include <asm/smp.h>
-#include "sclp.h"
-#define TAG "sclp_config: "
+#include "sclp.h"
struct conf_mgm_data {
u8 reserved;
@@ -31,7 +33,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work)
int cpu;
struct sys_device *sysdev;
- printk(KERN_WARNING TAG "cpu capability changed.\n");
+ pr_warning("cpu capability changed.\n");
get_online_cpus();
for_each_online_cpu(cpu) {
sysdev = get_cpu_sysdev(cpu);
@@ -78,7 +80,7 @@ static int __init sclp_conf_init(void)
return rc;
if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
- printk(KERN_WARNING TAG "no configuration management.\n");
+ pr_warning("no configuration management.\n");
sclp_unregister(&sclp_conf_register);
rc = -ENOSYS;
}
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c
index d887bd261d2..62c2647f37f 100644
--- a/drivers/s390/char/sclp_cpi_sys.c
+++ b/drivers/s390/char/sclp_cpi_sys.c
@@ -7,6 +7,9 @@
* Michael Ernst <mernst@de.ibm.com>
*/
+#define KMSG_COMPONENT "sclp_cpi"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
@@ -20,6 +23,7 @@
#include <linux/completion.h>
#include <asm/ebcdic.h>
#include <asm/sclp.h>
+
#include "sclp.h"
#include "sclp_rw.h"
#include "sclp_cpi_sys.h"
@@ -150,16 +154,16 @@ static int cpi_req(void)
wait_for_completion(&completion);
if (req->status != SCLP_REQ_DONE) {
- printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n",
- req->status);
+ pr_warning("request failed (status=0x%02x)\n",
+ req->status);
rc = -EIO;
goto out_free_req;
}
response = ((struct cpi_sccb *) req->sccb)->header.response_code;
if (response != 0x0020) {
- printk(KERN_WARNING "cpi: failed with "
- "response code 0x%x\n", response);
+ pr_warning("request failed with response code 0x%x\n",
+ response);
rc = -EIO;
}
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 8b854857ba0..6a1c58dc61a 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -5,15 +5,18 @@
* Author(s): Michael Holzheu
*/
+#define KMSG_COMPONENT "sclp_sdias"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/sched.h>
#include <asm/sclp.h>
#include <asm/debug.h>
#include <asm/ipl.h>
+
#include "sclp.h"
#include "sclp_rw.h"
#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
-#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
#define SDIAS_RETRIES 300
#define SDIAS_SLEEP_TICKS 50
@@ -131,7 +134,7 @@ int sclp_sdias_blk_count(void)
rc = sdias_sclp_send(&request);
if (rc) {
- ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+ pr_err("sclp_send failed for get_nr_blocks\n");
goto out;
}
if (sccb.hdr.response_code != 0x0020) {
@@ -145,7 +148,8 @@ int sclp_sdias_blk_count(void)
rc = sccb.evbuf.blk_cnt;
break;
default:
- ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+ pr_err("SCLP error: %x\n",
+ sccb.evbuf.event_status);
rc = -EIO;
goto out;
}
@@ -201,7 +205,7 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
rc = sdias_sclp_send(&request);
if (rc) {
- ERROR_MSG("sclp_send failed: %x\n", rc);
+ pr_err("sclp_send failed: %x\n", rc);
goto out;
}
if (sccb.hdr.response_code != 0x0020) {
@@ -219,9 +223,9 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
case EVSTATE_NO_DATA:
TRACE("no data\n");
default:
- ERROR_MSG("Error from SCLP while copying hsa. "
- "Event status = %x\n",
- sccb.evbuf.event_status);
+ pr_err("Error from SCLP while copying hsa. "
+ "Event status = %x\n",
+ sccb.evbuf.event_status);
rc = -EIO;
}
out:
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 9854f19f5e6..a839aa531d7 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -583,23 +583,6 @@ sclp_vt220_chars_in_buffer(struct tty_struct *tty)
return count;
}
-static void
-__sclp_vt220_flush_buffer(void)
-{
- unsigned long flags;
-
- sclp_vt220_emit_current();
- spin_lock_irqsave(&sclp_vt220_lock, flags);
- if (timer_pending(&sclp_vt220_timer))
- del_timer(&sclp_vt220_timer);
- while (sclp_vt220_outqueue_count > 0) {
- spin_unlock_irqrestore(&sclp_vt220_lock, flags);
- sclp_sync_wait();
- spin_lock_irqsave(&sclp_vt220_lock, flags);
- }
- spin_unlock_irqrestore(&sclp_vt220_lock, flags);
-}
-
/*
* Pass on all buffers to the hardware. Return only when there are no more
* buffers pending.
@@ -745,6 +728,22 @@ sclp_vt220_con_device(struct console *c, int *index)
return sclp_vt220_driver;
}
+static void __sclp_vt220_flush_buffer(void)
+{
+ unsigned long flags;
+
+ sclp_vt220_emit_current();
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ if (timer_pending(&sclp_vt220_timer))
+ del_timer(&sclp_vt220_timer);
+ while (sclp_vt220_outqueue_count > 0) {
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+ sclp_sync_wait();
+ spin_lock_irqsave(&sclp_vt220_lock, flags);
+ }
+ spin_unlock_irqrestore(&sclp_vt220_lock, flags);
+}
+
static int
sclp_vt220_notify(struct notifier_block *self,
unsigned long event, void *data)
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c
index 09e7d9bf438..a6087cec55b 100644
--- a/drivers/s390/char/vmcp.c
+++ b/drivers/s390/char/vmcp.c
@@ -11,12 +11,14 @@
* The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS
*/
+#define KMSG_COMPONENT "vmcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
-#include <linux/smp_lock.h>
#include <asm/cpcmd.h>
#include <asm/debug.h>
#include <asm/uaccess.h>
@@ -26,8 +28,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Borntraeger <borntraeger@de.ibm.com>");
MODULE_DESCRIPTION("z/VM CP interface");
-#define PRINTK_HEADER "vmcp: "
-
static debug_info_t *vmcp_debug;
static int vmcp_open(struct inode *inode, struct file *file)
@@ -41,13 +41,11 @@ static int vmcp_open(struct inode *inode, struct file *file)
if (!session)
return -ENOMEM;
- lock_kernel();
session->bufsize = PAGE_SIZE;
session->response = NULL;
session->resp_size = 0;
mutex_init(&session->mutex);
file->private_data = session;
- unlock_kernel();
return nonseekable_open(inode, file);
}
@@ -193,7 +191,8 @@ static int __init vmcp_init(void)
int ret;
if (!MACHINE_IS_VM) {
- PRINT_WARN("z/VM CP interface is only available under z/VM\n");
+ pr_warning("The z/VM CP interface device driver cannot be "
+ "loaded without z/VM\n");
return -ENODEV;
}
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 24762727bc2..aabbeb909cc 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -10,6 +10,10 @@
* Stefan Weinhuber <wein@de.ibm.com>
*
*/
+
+#define KMSG_COMPONENT "vmlogrdr"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
@@ -28,8 +32,6 @@
#include <linux/smp_lock.h>
#include <linux/string.h>
-
-
MODULE_AUTHOR
("(C) 2004 IBM Corporation by Xenia Tkatschow (xenia@us.ibm.com)\n"
" Stefan Weinhuber (wein@de.ibm.com)");
@@ -174,8 +176,7 @@ static void vmlogrdr_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
struct vmlogrdr_priv_t * logptr = path->private;
u8 reason = (u8) ipuser[8];
- printk (KERN_ERR "vmlogrdr: connection severed with"
- " reason %i\n", reason);
+ pr_err("vmlogrdr: connection severed with reason %i\n", reason);
iucv_path_sever(path, NULL);
kfree(path);
@@ -333,8 +334,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,1,logptr->autopurge);
if (ret)
- printk (KERN_WARNING "vmlogrdr: failed to start "
- "recording automatically\n");
+ pr_warning("vmlogrdr: failed to start "
+ "recording automatically\n");
}
/* create connection to the system service */
@@ -345,9 +346,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
logptr->system_service, NULL, NULL,
logptr);
if (connect_rc) {
- printk (KERN_ERR "vmlogrdr: iucv connection to %s "
- "failed with rc %i \n", logptr->system_service,
- connect_rc);
+ pr_err("vmlogrdr: iucv connection to %s "
+ "failed with rc %i \n",
+ logptr->system_service, connect_rc);
goto out_path;
}
@@ -388,8 +389,8 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp)
if (logptr->autorecording) {
ret = vmlogrdr_recording(logptr,0,logptr->autopurge);
if (ret)
- printk (KERN_WARNING "vmlogrdr: failed to stop "
- "recording automatically\n");
+ pr_warning("vmlogrdr: failed to stop "
+ "recording automatically\n");
}
logptr->dev_in_use = 0;
@@ -823,8 +824,7 @@ static int __init vmlogrdr_init(void)
dev_t dev;
if (! MACHINE_IS_VM) {
- printk (KERN_ERR "vmlogrdr: not running under VM, "
- "driver not loaded.\n");
+ pr_err("not running under VM, driver not loaded.\n");
return -ENODEV;
}
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 9020eba620e..5dcef81fc9d 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -8,6 +8,9 @@
* Frank Munzert <munzert@de.ibm.com>
*/
+#define KMSG_COMPONENT "vmur"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/cdev.h>
#include <linux/smp_lock.h>
@@ -40,8 +43,6 @@ MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver");
MODULE_LICENSE("GPL");
-#define PRINTK_HEADER "vmur: "
-
static dev_t ur_first_dev_maj_min;
static struct class *vmur_class;
static struct debug_info *vmur_dbf;
@@ -987,7 +988,8 @@ static int __init ur_init(void)
dev_t dev;
if (!MACHINE_IS_VM) {
- PRINT_ERR("%s is only available under z/VM.\n", ur_banner);
+ pr_err("The %s cannot be loaded without z/VM\n",
+ ur_banner);
return -ENODEV;
}
@@ -1006,7 +1008,8 @@ static int __init ur_init(void)
rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
if (rc) {
- PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc);
+ pr_err("Kernel function alloc_chrdev_region failed with "
+ "error code %d\n", rc);
goto fail_unregister_driver;
}
ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
@@ -1016,7 +1019,7 @@ static int __init ur_init(void)
rc = PTR_ERR(vmur_class);
goto fail_unregister_region;
}
- PRINT_INFO("%s loaded.\n", ur_banner);
+ pr_info("%s loaded.\n", ur_banner);
return 0;
fail_unregister_region:
@@ -1034,7 +1037,7 @@ static void __exit ur_exit(void)
unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
ccw_driver_unregister(&ur_driver);
debug_unregister(vmur_dbf);
- PRINT_INFO("%s unloaded.\n", ur_banner);
+ pr_info("%s unloaded.\n", ur_banner);
}
module_init(ur_init);
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 7fd84be1193..eefc6611412 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -9,6 +9,9 @@
* Author(s): Michael Holzheu
*/
+#define KMSG_COMPONENT "zdump"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/utsname.h>
@@ -24,8 +27,6 @@
#include "sclp.h"
#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
-#define MSG(x...) printk( KERN_ALERT x )
-#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
#define TO_USER 0
#define TO_KERNEL 1
@@ -563,19 +564,19 @@ static int __init sys_info_init(enum arch_id arch)
switch (arch) {
case ARCH_S390X:
- MSG("DETECTED 'S390X (64 bit) OS'\n");
+ pr_alert("DETECTED 'S390X (64 bit) OS'\n");
sys_info.sa_base = SAVE_AREA_BASE_S390X;
sys_info.sa_size = sizeof(struct save_area_s390x);
set_s390x_lc_mask(&sys_info.lc_mask);
break;
case ARCH_S390:
- MSG("DETECTED 'S390 (32 bit) OS'\n");
+ pr_alert("DETECTED 'S390 (32 bit) OS'\n");
sys_info.sa_base = SAVE_AREA_BASE_S390;
sys_info.sa_size = sizeof(struct save_area_s390);
set_s390_lc_mask(&sys_info.lc_mask);
break;
default:
- ERROR_MSG("unknown architecture 0x%x.\n",arch);
+ pr_alert("0x%x is an unknown architecture.\n",arch);
return -EINVAL;
}
sys_info.arch = arch;
@@ -674,7 +675,8 @@ static int __init zcore_init(void)
#ifndef __s390x__
if (arch == ARCH_S390X) {
- ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+ pr_alert("The 32-bit dump tool cannot be used for a "
+ "64-bit system\n");
rc = -EINVAL;
goto fail;
}
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 2f547b840ef..fe00be3675c 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -9,6 +9,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
@@ -50,9 +53,10 @@ static int blacklist_range(range_action action, unsigned int from_ssid,
{
if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
if (msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore range "
- "0.%x.%04x-0.%x.%04x\n", from_ssid, from,
- to_ssid, to);
+ pr_warning("0.%x.%04x to 0.%x.%04x is not a valid "
+ "range for cio_ignore\n", from_ssid, from,
+ to_ssid, to);
+
return 1;
}
@@ -140,8 +144,8 @@ static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
rc = 0;
out:
if (rc && msgtrigger)
- printk(KERN_WARNING "cio: Invalid cio_ignore device '%s'\n",
- str);
+ pr_warning("%s is not a valid device for the cio_ignore "
+ "kernel parameter\n", str);
return rc;
}
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 3ac2c2019f5..918e6fce257 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -19,6 +19,8 @@
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
+#define CCW_BUS_ID_SIZE 20
+
/* In Linux 2.4, we had a channel device layer called "chandev"
* that did all sorts of obscure stuff for networking devices.
* This is another driver that serves as a replacement for just
@@ -89,15 +91,23 @@ ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const
gdev = to_ccwgroupdev(dev);
- if (gdev->state != CCWGROUP_OFFLINE)
- return -EINVAL;
-
+ /* Prevent concurrent online/offline processing and ungrouping. */
+ if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
+ return -EAGAIN;
+ if (gdev->state != CCWGROUP_OFFLINE) {
+ rc = -EINVAL;
+ goto out;
+ }
/* Note that we cannot unregister the device from one of its
* attribute methods, so we have to use this roundabout approach.
*/
rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
- if (rc)
- count = rc;
+out:
+ if (rc) {
+ /* Release onoff "lock" when ungrouping failed. */
+ atomic_set(&gdev->onoff, 0);
+ return rc;
+ }
return count;
}
@@ -172,7 +182,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
len = end - start + 1;
end++;
}
- if (len < BUS_ID_SIZE) {
+ if (len < CCW_BUS_ID_SIZE) {
strlcpy(bus_id, start, len);
rc = 0;
} else
@@ -181,7 +191,7 @@ static int __get_next_bus_id(const char **buf, char *bus_id)
return rc;
}
-static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
+static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE])
{
int cssid, ssid, devno;
@@ -213,7 +223,7 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
{
struct ccwgroup_device *gdev;
int rc, i;
- char tmp_bus_id[BUS_ID_SIZE];
+ char tmp_bus_id[CCW_BUS_ID_SIZE];
const char *curr_buf;
gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 29826fdd47b..ebab6ea4659 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -8,6 +8,9 @@
* Arnd Bergmann (arndb@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
@@ -333,6 +336,7 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
struct chp_config_data *data;
struct chp_id chpid;
int num;
+ char *events[3] = {"configure", "deconfigure", "cancel deconfigure"};
CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n");
if (sei_area->rs != 0)
@@ -343,8 +347,8 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
if (!chp_test_bit(data->map, num))
continue;
chpid.id = num;
- printk(KERN_WARNING "cio: processing configure event %d for "
- "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id);
+ pr_notice("Processing %s for channel path %x.%02x\n",
+ events[data->op], chpid.cssid, chpid.id);
switch (data->op) {
case 0:
chp_cfg_schedule(chpid, 1);
diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c
index f49f0e502b8..0a2f2edafc0 100644
--- a/drivers/s390/cio/chsc_sch.c
+++ b/drivers/s390/cio/chsc_sch.c
@@ -61,7 +61,7 @@ static void chsc_subchannel_irq(struct subchannel *sch)
}
private->request = NULL;
memcpy(&request->irb, irb, sizeof(*irb));
- stsch(sch->schid, &sch->schib);
+ cio_update_schib(sch);
complete(&request->completion);
put_device(&sch->dev);
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 3db2c386546..8a8df755296 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -9,6 +9,9 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -104,44 +107,6 @@ cio_get_options (struct subchannel *sch)
return flags;
}
-/*
- * Use tpi to get a pending interrupt, call the interrupt handler and
- * return a pointer to the subchannel structure.
- */
-static int
-cio_tpi(void)
-{
- struct tpi_info *tpi_info;
- struct subchannel *sch;
- struct irb *irb;
- int irq_context;
-
- tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
- if (tpi (NULL) != 1)
- return 0;
- irb = (struct irb *) __LC_IRB;
- /* Store interrupt response block to lowcore. */
- if (tsch (tpi_info->schid, irb) != 0)
- /* Not status pending or not operational. */
- return 1;
- sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
- if (!sch)
- return 1;
- irq_context = in_interrupt();
- if (!irq_context)
- local_bh_disable();
- irq_enter ();
- spin_lock(sch->lock);
- memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
- if (sch->driver && sch->driver->irq)
- sch->driver->irq(sch);
- spin_unlock(sch->lock);
- irq_exit ();
- if (!irq_context)
- _local_bh_enable();
- return 1;
-}
-
static int
cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
{
@@ -152,11 +117,13 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
else
sch->lpm = 0;
- stsch (sch->schid, &sch->schib);
-
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
+
+ if (cio_update_schib(sch))
+ return -ENODEV;
+
sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -354,7 +321,8 @@ cio_cancel (struct subchannel *sch)
switch (ccode) {
case 0: /* success */
/* Update information in scsw. */
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ return -ENODEV;
return 0;
case 1: /* status pending */
return -EBUSY;
@@ -365,30 +333,70 @@ cio_cancel (struct subchannel *sch)
}
}
+
+static void cio_apply_config(struct subchannel *sch, struct schib *schib)
+{
+ schib->pmcw.intparm = sch->config.intparm;
+ schib->pmcw.mbi = sch->config.mbi;
+ schib->pmcw.isc = sch->config.isc;
+ schib->pmcw.ena = sch->config.ena;
+ schib->pmcw.mme = sch->config.mme;
+ schib->pmcw.mp = sch->config.mp;
+ schib->pmcw.csense = sch->config.csense;
+ schib->pmcw.mbfc = sch->config.mbfc;
+ if (sch->config.mbfc)
+ schib->mba = sch->config.mba;
+}
+
+static int cio_check_config(struct subchannel *sch, struct schib *schib)
+{
+ return (schib->pmcw.intparm == sch->config.intparm) &&
+ (schib->pmcw.mbi == sch->config.mbi) &&
+ (schib->pmcw.isc == sch->config.isc) &&
+ (schib->pmcw.ena == sch->config.ena) &&
+ (schib->pmcw.mme == sch->config.mme) &&
+ (schib->pmcw.mp == sch->config.mp) &&
+ (schib->pmcw.csense == sch->config.csense) &&
+ (schib->pmcw.mbfc == sch->config.mbfc) &&
+ (!sch->config.mbfc || (schib->mba == sch->config.mba));
+}
+
/*
- * Function: cio_modify
- * Issues a "Modify Subchannel" on the specified subchannel
+ * cio_commit_config - apply configuration to the subchannel
*/
-int
-cio_modify (struct subchannel *sch)
+int cio_commit_config(struct subchannel *sch)
{
- int ccode, retry, ret;
+ struct schib schib;
+ int ccode, retry, ret = 0;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
- ret = 0;
for (retry = 0; retry < 5; retry++) {
- ccode = msch_err (sch->schid, &sch->schib);
- if (ccode < 0) /* -EIO if msch gets a program check. */
+ /* copy desired changes to local schib */
+ cio_apply_config(sch, &schib);
+ ccode = msch_err(sch->schid, &schib);
+ if (ccode < 0) /* -EIO if msch gets a program check. */
return ccode;
switch (ccode) {
case 0: /* successfull */
- return 0;
- case 1: /* status pending */
+ if (stsch(sch->schid, &schib) ||
+ !css_sch_is_valid(&schib))
+ return -ENODEV;
+ if (cio_check_config(sch, &schib)) {
+ /* commit changes from local schib */
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+ }
+ ret = -EAGAIN;
+ break;
+ case 1: /* status pending */
return -EBUSY;
- case 2: /* busy */
- udelay (100); /* allow for recovery */
+ case 2: /* busy */
+ udelay(100); /* allow for recovery */
ret = -EBUSY;
break;
- case 3: /* not operational */
+ case 3: /* not operational */
return -ENODEV;
}
}
@@ -396,6 +404,23 @@ cio_modify (struct subchannel *sch)
}
/**
+ * cio_update_schib - Perform stsch and update schib if subchannel is valid.
+ * @sch: subchannel on which to perform stsch
+ * Return zero on success, -ENODEV otherwise.
+ */
+int cio_update_schib(struct subchannel *sch)
+{
+ struct schib schib;
+
+ if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+ return -ENODEV;
+
+ memcpy(&sch->schib, &schib, sizeof(schib));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cio_update_schib);
+
+/**
* cio_enable_subchannel - enable a subchannel.
* @sch: subchannel to be enabled
* @intparm: interruption parameter to set
@@ -403,7 +428,6 @@ cio_modify (struct subchannel *sch)
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
char dbf_txt[15];
- int ccode;
int retry;
int ret;
@@ -412,33 +436,27 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
if (sch_is_pseudo_sch(sch))
return -EINVAL;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode)
+ if (cio_update_schib(sch))
return -ENODEV;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 1;
- sch->schib.pmcw.isc = sch->isc;
- sch->schib.pmcw.intparm = intparm;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EIO)
+ sch->config.ena = 1;
+ sch->config.isc = sch->isc;
+ sch->config.intparm = intparm;
+
+ for (retry = 0; retry < 3; retry++) {
+ ret = cio_commit_config(sch);
+ if (ret == -EIO) {
/*
- * Got a program check in cio_modify. Try without
+ * Got a program check in msch. Try without
* the concurrent sense bit the next time.
*/
- sch->schib.pmcw.csense = 0;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (sch->schib.pmcw.ena)
- break;
- }
- if (ret == -EBUSY) {
+ sch->config.csense = 0;
+ } else if (ret == -EBUSY) {
struct irb irb;
if (tsch(sch->schid, &irb) != 0)
break;
- }
+ } else
+ break;
}
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
@@ -453,8 +471,6 @@ EXPORT_SYMBOL_GPL(cio_enable_subchannel);
int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
- int ccode;
- int retry;
int ret;
CIO_TRACE_EVENT (2, "dissch");
@@ -462,8 +478,7 @@ int cio_disable_subchannel(struct subchannel *sch)
if (sch_is_pseudo_sch(sch))
return 0;
- ccode = stsch (sch->schid, &sch->schib);
- if (ccode == 3) /* Not operational. */
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
@@ -473,24 +488,9 @@ int cio_disable_subchannel(struct subchannel *sch)
*/
return -EBUSY;
- for (retry = 5, ret = 0; retry > 0; retry--) {
- sch->schib.pmcw.ena = 0;
- ret = cio_modify(sch);
- if (ret == -ENODEV)
- break;
- if (ret == -EBUSY)
- /*
- * The subchannel is busy or status pending.
- * We'll disable when the next interrupt was delivered
- * via the state machine.
- */
- break;
- if (ret == 0) {
- stsch (sch->schid, &sch->schib);
- if (!sch->schib.pmcw.ena)
- break;
- }
- }
+ sch->config.ena = 0;
+ ret = cio_commit_config(sch);
+
sprintf (dbf_txt, "ret:%d", ret);
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
@@ -687,6 +687,43 @@ static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
+/*
+ * Use tpi to get a pending interrupt, call the interrupt handler and
+ * return a pointer to the subchannel structure.
+ */
+static int cio_tpi(void)
+{
+ struct tpi_info *tpi_info;
+ struct subchannel *sch;
+ struct irb *irb;
+ int irq_context;
+
+ tpi_info = (struct tpi_info *) __LC_SUBCHANNEL_ID;
+ if (tpi(NULL) != 1)
+ return 0;
+ irb = (struct irb *) __LC_IRB;
+ /* Store interrupt response block to lowcore. */
+ if (tsch(tpi_info->schid, irb) != 0)
+ /* Not status pending or not operational. */
+ return 1;
+ sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
+ if (!sch)
+ return 1;
+ irq_context = in_interrupt();
+ if (!irq_context)
+ local_bh_disable();
+ irq_enter();
+ spin_lock(sch->lock);
+ memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
+ if (sch->driver && sch->driver->irq)
+ sch->driver->irq(sch);
+ spin_unlock(sch->lock);
+ irq_exit();
+ if (!irq_context)
+ _local_bh_enable();
+ return 1;
+}
+
void *cio_get_console_priv(void)
{
return &console_priv;
@@ -780,7 +817,7 @@ cio_probe_console(void)
sch_no = cio_get_console_sch_no();
if (sch_no == -1) {
console_subchannel_in_use = 0;
- printk(KERN_WARNING "cio: No ccw console found!\n");
+ pr_warning("No CCW console was found\n");
return ERR_PTR(-ENODEV);
}
memset(&console_subchannel, 0, sizeof(struct subchannel));
@@ -796,10 +833,9 @@ cio_probe_console(void)
* enable console I/O-interrupt subclass
*/
isc_register(CONSOLE_ISC);
- console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
- console_subchannel.schib.pmcw.intparm =
- (u32)(addr_t)&console_subchannel;
- ret = cio_modify(&console_subchannel);
+ console_subchannel.config.isc = CONSOLE_ISC;
+ console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
+ ret = cio_commit_config(&console_subchannel);
if (ret) {
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
@@ -811,8 +847,8 @@ cio_probe_console(void)
void
cio_release_console(void)
{
- console_subchannel.schib.pmcw.intparm = 0;
- cio_modify(&console_subchannel);
+ console_subchannel.config.intparm = 0;
+ cio_commit_config(&console_subchannel);
isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
}
@@ -852,7 +888,8 @@ __disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
cc = msch(schid, schib);
if (cc)
return (cc==3?-ENODEV:-EBUSY);
- stsch(schid, schib);
+ if (stsch(schid, schib) || !css_sch_is_valid(schib))
+ return -ENODEV;
if (!schib->pmcw.ena)
return 0;
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 0fb24784e92..5150fba742a 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -45,6 +45,19 @@ struct pmcw {
/* ... in an operand exception. */
} __attribute__ ((packed));
+/* Target SCHIB configuration. */
+struct schib_config {
+ u64 mba;
+ u32 intparm;
+ u16 mbi;
+ u32 isc:3;
+ u32 ena:1;
+ u32 mme:2;
+ u32 mp:1;
+ u32 csense:1;
+ u32 mbfc:1;
+} __attribute__ ((packed));
+
/*
* subchannel information block
*/
@@ -82,6 +95,8 @@ struct subchannel {
struct device dev; /* entry in device tree */
struct css_driver *driver;
void *private; /* private per subchannel type data */
+ struct work_struct work;
+ struct schib_config config;
} __attribute__ ((aligned(8)));
#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
@@ -100,7 +115,8 @@ extern int cio_start_key (struct subchannel *, struct ccw1 *, __u8, __u8);
extern int cio_cancel (struct subchannel *);
extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
-extern int cio_modify (struct subchannel *);
+extern int cio_update_schib(struct subchannel *sch);
+extern int cio_commit_config(struct subchannel *sch);
int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch);
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index a90b28c0be5..dc98b2c6386 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -25,6 +25,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/bootmem.h>
#include <linux/device.h>
#include <linux/init.h>
@@ -185,56 +188,19 @@ 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)
{
- int ret;
- int retry;
struct subchannel *sch;
- struct schib *schib;
sch = to_subchannel(cdev->dev.parent);
- schib = &sch->schib;
- /* msch can silently fail, so do it again if necessary */
- for (retry = 0; retry < 3; retry++) {
- /* prepare schib */
- stsch(sch->schid, schib);
- schib->pmcw.mme = mme;
- schib->pmcw.mbfc = mbfc;
- /* address can be either a block address or a block index */
- if (mbfc)
- schib->mba = address;
- else
- schib->pmcw.mbi = address;
-
- /* try to submit it */
- switch(ret = msch_err(sch->schid, schib)) {
- case 0:
- break;
- case 1:
- case 2: /* in I/O or status pending */
- ret = -EBUSY;
- break;
- case 3: /* subchannel is no longer valid */
- ret = -ENODEV;
- break;
- default: /* msch caught an exception */
- ret = -EINVAL;
- break;
- }
- stsch(sch->schid, schib); /* restore the schib */
-
- if (ret)
- break;
- /* check if it worked */
- if (schib->pmcw.mme == mme &&
- schib->pmcw.mbfc == mbfc &&
- (mbfc ? (schib->mba == address)
- : (schib->pmcw.mbi == address)))
- return 0;
+ sch->config.mme = mme;
+ sch->config.mbfc = mbfc;
+ /* address can be either a block address or a block index */
+ if (mbfc)
+ sch->config.mba = address;
+ else
+ sch->config.mbi = address;
- ret = -EINVAL;
- }
-
- return ret;
+ return cio_commit_config(sch);
}
struct set_schib_struct {
@@ -338,7 +304,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
@@ -1359,9 +1325,8 @@ static int __init init_cmf(void)
default:
return 1;
}
-
- printk(KERN_INFO "cio: Channel measurement facility using %s "
- "format (%s)\n", format_string, detect_string);
+ pr_info("Channel measurement facility initialized using format "
+ "%s (mode %s)\n", format_string, detect_string);
return 0;
}
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 76bbb1e74c2..8019288bc6d 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -6,6 +6,10 @@
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
+
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
@@ -128,8 +132,8 @@ css_free_subchannel(struct subchannel *sch)
{
if (sch) {
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
kfree(sch->lock);
kfree(sch);
}
@@ -844,8 +848,8 @@ out:
s390_unregister_crw_handler(CRW_RSC_CSS);
chsc_free_sei_area();
kfree(slow_subchannel_set);
- printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
- ret);
+ pr_alert("The CSS device driver initialization failed with "
+ "errno=%d\n", ret);
return ret;
}
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 4e4008325e2..23d5752349b 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -376,19 +376,23 @@ int ccw_device_set_offline(struct ccw_device *cdev)
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
}
spin_unlock_irq(cdev->ccwlock);
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
return ret;
}
spin_unlock_irq(cdev->ccwlock);
- if (ret == 0)
+ if (ret == 0) {
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
- else {
+ /* Give up reference from ccw_device_set_online(). */
+ put_device(&cdev->dev);
+ } else {
CIO_MSG_EVENT(0, "ccw_device_offline returned %d, "
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
cdev->online = 1;
}
- return ret;
+ return ret;
}
/**
@@ -411,6 +415,9 @@ int ccw_device_set_online(struct ccw_device *cdev)
return -ENODEV;
if (cdev->online || !cdev->drv)
return -EINVAL;
+ /* Hold on to an extra reference while device is online. */
+ if (!get_device(&cdev->dev))
+ return -ENODEV;
spin_lock_irq(cdev->ccwlock);
ret = ccw_device_online(cdev);
@@ -422,10 +429,15 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return ret;
}
- if (cdev->private->state != DEV_STATE_ONLINE)
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return -ENODEV;
+ }
if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
cdev->online = 1;
return 0;
@@ -440,6 +452,8 @@ int ccw_device_set_online(struct ccw_device *cdev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up online reference since onlining failed. */
+ put_device(&cdev->dev);
return (ret == 0) ? -ENODEV : ret;
}
@@ -704,6 +718,8 @@ ccw_device_release(struct device *dev)
struct ccw_device *cdev;
cdev = to_ccwdev(dev);
+ /* Release reference of parent subchannel. */
+ put_device(cdev->dev.parent);
kfree(cdev->private);
kfree(cdev);
}
@@ -735,8 +751,8 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
/* Do first half of device_register. */
device_initialize(&cdev->dev);
if (!get_device(&sch->dev)) {
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
+ /* Release reference from device_initialize(). */
+ put_device(&cdev->dev);
return -ENODEV;
}
return 0;
@@ -778,37 +794,55 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
struct subchannel *other_sch;
int ret;
- other_sch = to_subchannel(get_device(cdev->dev.parent));
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ other_sch = to_subchannel(cdev->dev.parent);
+ /* Note: device_move() changes cdev->dev.parent */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&other_sch->dev);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_set_cdev(other_sch, NULL);
/* No need to keep a subchannel without ccw device around. */
css_sch_device_unregister(other_sch);
- put_device(&other_sch->dev);
sch_attach_device(sch, cdev);
+ /* Put reference for old parent. */
+ put_device(&other_sch->dev);
}
static void sch_attach_orphaned_device(struct subchannel *sch,
struct ccw_device *cdev)
{
int ret;
+ struct subchannel *pseudo_sch;
- /* Try to move the ccw device to its new subchannel. */
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
+ pseudo_sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
ret = device_move(&cdev->dev, &sch->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
"failed (ret=%d)!\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Put reference for new parent. */
+ put_device(&sch->dev);
return;
}
sch_attach_device(sch, cdev);
+ /* Put reference on pseudo subchannel. */
+ put_device(&pseudo_sch->dev);
}
static void sch_create_and_recog_new_device(struct subchannel *sch)
@@ -830,9 +864,11 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
spin_lock_irq(sch->lock);
sch_set_cdev(sch, NULL);
spin_unlock_irq(sch->lock);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
css_sch_device_unregister(sch);
+ /* Put reference from io_subchannel_create_ccwdev(). */
+ put_device(&sch->dev);
+ /* Give up initial reference. */
+ put_device(&cdev->dev);
}
}
@@ -854,15 +890,20 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ /* Increase refcount for pseudo subchannel. */
+ get_device(&css->pseudo_subchannel->dev);
/*
* Move the orphaned ccw device to the orphanage so the replacing
* ccw device can take its place on the subchannel.
+ * Note: device_move() changes cdev->dev.parent
*/
ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
if (ret) {
CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
"(ret=%d)!\n", cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
+ /* Decrease refcount for pseudo subchannel again. */
+ put_device(&css->pseudo_subchannel->dev);
return;
}
cdev->ccwlock = css->pseudo_subchannel->lock;
@@ -875,17 +916,23 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
if (replacing_cdev) {
sch_attach_disconnected_device(sch, replacing_cdev);
/* Release reference from get_disc_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
if (replacing_cdev) {
sch_attach_orphaned_device(sch, replacing_cdev);
/* Release reference from get_orphaned_ccwdev_by_dev_id() */
- put_device(&cdev->dev);
+ put_device(&replacing_cdev->dev);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
return;
}
sch_create_and_recog_new_device(sch);
+ /* Release reference of subchannel from old cdev. */
+ put_device(&sch->dev);
}
/*
@@ -903,6 +950,14 @@ io_subchannel_register(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
sch = to_subchannel(cdev->dev.parent);
+ /*
+ * Check if subchannel is still registered. It may have become
+ * unregistered if a machine check hit us after finishing
+ * device recognition but before the register work could be
+ * queued.
+ */
+ if (!device_is_registered(&sch->dev))
+ goto out_err;
css_update_ssd_info(sch);
/*
* io_subchannel_register() will also be called after device
@@ -910,7 +965,7 @@ io_subchannel_register(struct work_struct *work)
* be registered). We need to reprobe since we may now have sense id
* information.
*/
- if (klist_node_attached(&cdev->dev.knode_parent)) {
+ if (device_is_registered(&cdev->dev)) {
if (!cdev->drv) {
ret = device_reprobe(&cdev->dev);
if (ret)
@@ -934,22 +989,19 @@ io_subchannel_register(struct work_struct *work)
CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, ret);
- put_device(&cdev->dev);
spin_lock_irqsave(sch->lock, flags);
sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
- kfree (cdev->private);
- kfree (cdev);
- put_device(&sch->dev);
- if (atomic_dec_and_test(&ccw_device_init_count))
- wake_up(&ccw_device_init_wq);
- return;
+ /* Release initial device reference. */
+ put_device(&cdev->dev);
+ goto out_err;
}
- put_device(&cdev->dev);
out:
cdev->private->flags.recog_done = 1;
- put_device(&sch->dev);
wake_up(&cdev->private->wait_q);
+out_err:
+ /* Release reference for workqueue processing. */
+ put_device(&cdev->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
}
@@ -968,8 +1020,8 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
sch = to_subchannel(cdev->dev.parent);
css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
/* Release cdev reference for workqueue processing.*/
put_device(&cdev->dev);
/* Release subchannel reference for local processing. */
@@ -998,8 +1050,6 @@ io_subchannel_recog_done(struct ccw_device *cdev)
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_call_sch_unregister);
queue_work(slow_path_wq, &cdev->private->kick_work);
- /* Release subchannel reference for asynchronous recognition. */
- put_device(&sch->dev);
if (atomic_dec_and_test(&ccw_device_init_count))
wake_up(&ccw_device_init_wq);
break;
@@ -1070,10 +1120,15 @@ static void ccw_device_move_to_sch(struct work_struct *work)
priv = container_of(work, struct ccw_device_private, kick_work);
sch = priv->sch;
cdev = priv->cdev;
- former_parent = ccw_device_is_orphan(cdev) ?
- NULL : to_subchannel(get_device(cdev->dev.parent));
+ former_parent = to_subchannel(cdev->dev.parent);
+ /* Get reference for new parent. */
+ if (!get_device(&sch->dev))
+ return;
mutex_lock(&sch->reg_mutex);
- /* Try to move the ccw device to its new subchannel. */
+ /*
+ * Try to move the ccw device to its new subchannel.
+ * Note: device_move() changes cdev->dev.parent
+ */
rc = device_move(&cdev->dev, &sch->dev);
mutex_unlock(&sch->reg_mutex);
if (rc) {
@@ -1083,21 +1138,23 @@ static void ccw_device_move_to_sch(struct work_struct *work)
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schid.sch_no, rc);
css_sch_device_unregister(sch);
+ /* Put reference for new parent again. */
+ put_device(&sch->dev);
goto out;
}
- if (former_parent) {
+ if (!sch_is_pseudo_sch(former_parent)) {
spin_lock_irq(former_parent->lock);
sch_set_cdev(former_parent, NULL);
spin_unlock_irq(former_parent->lock);
css_sch_device_unregister(former_parent);
/* Reset intparm to zeroes. */
- former_parent->schib.pmcw.intparm = 0;
- cio_modify(former_parent);
+ former_parent->config.intparm = 0;
+ cio_commit_config(former_parent);
}
sch_attach_device(sch, cdev);
out:
- if (former_parent)
- put_device(&former_parent->dev);
+ /* Put reference for old parent. */
+ put_device(&former_parent->dev);
put_device(&cdev->dev);
}
@@ -1113,6 +1170,15 @@ static void io_subchannel_irq(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
+void io_subchannel_init_config(struct subchannel *sch)
+{
+ memset(&sch->config, 0, sizeof(sch->config));
+ sch->config.csense = 1;
+ /* Use subchannel mp mode when there is more than 1 installed CHPID. */
+ if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
+ sch->config.mp = 1;
+}
+
static void io_subchannel_init_fields(struct subchannel *sch)
{
if (cio_is_console(sch->schid))
@@ -1127,18 +1193,34 @@ static void io_subchannel_init_fields(struct subchannel *sch)
sch->schib.pmcw.dev, sch->schid.ssid,
sch->schid.sch_no, sch->schib.pmcw.pim,
sch->schib.pmcw.pam, sch->schib.pmcw.pom);
- /* Initially set up some fields in the pmcw. */
- sch->schib.pmcw.ena = 0;
- sch->schib.pmcw.csense = 1; /* concurrent sense */
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1; /* multipath mode */
- /* clean up possible residual cmf stuff */
- sch->schib.pmcw.mme = 0;
- sch->schib.pmcw.mbfc = 0;
- sch->schib.pmcw.mbi = 0;
- sch->schib.mba = 0;
+
+ io_subchannel_init_config(sch);
}
+static void io_subchannel_do_unreg(struct work_struct *work)
+{
+ struct subchannel *sch;
+
+ sch = container_of(work, struct subchannel, work);
+ css_sch_device_unregister(sch);
+ /* Reset intparm to zeroes. */
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
+ put_device(&sch->dev);
+}
+
+/* Schedule unregister if we have no cdev. */
+static void io_subchannel_schedule_removal(struct subchannel *sch)
+{
+ get_device(&sch->dev);
+ INIT_WORK(&sch->work, io_subchannel_do_unreg);
+ queue_work(slow_path_wq, &sch->work);
+}
+
+/*
+ * Note: We always return 0 so that we bind to the device even on error.
+ * This is needed so that our remove function is called on unregister.
+ */
static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
@@ -1168,9 +1250,8 @@ static int io_subchannel_probe(struct subchannel *sch)
ccw_device_register(cdev);
/*
* Check if the device is already online. If it is
- * the reference count needs to be corrected
- * (see ccw_device_online and css_init_done for the
- * ugly details).
+ * the reference count needs to be corrected since we
+ * didn't obtain a reference in ccw_device_set_online.
*/
if (cdev->private->state != DEV_STATE_NOT_OPER &&
cdev->private->state != DEV_STATE_OFFLINE &&
@@ -1179,23 +1260,24 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
io_subchannel_init_fields(sch);
- /*
- * First check if a fitting device may be found amongst the
- * disconnected devices or in the orphanage.
- */
- dev_id.devno = sch->schib.pmcw.dev;
- dev_id.ssid = sch->schid.ssid;
+ rc = cio_commit_config(sch);
+ if (rc)
+ goto out_schedule;
rc = sysfs_create_group(&sch->dev.kobj,
&io_subchannel_attr_group);
if (rc)
- return rc;
+ goto out_schedule;
/* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
- if (!sch->private) {
- rc = -ENOMEM;
+ if (!sch->private)
goto out_err;
- }
+ /*
+ * First check if a fitting device may be found amongst the
+ * disconnected devices or in the orphanage.
+ */
+ dev_id.devno = sch->schib.pmcw.dev;
+ dev_id.ssid = sch->schid.ssid;
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1213,24 +1295,21 @@ static int io_subchannel_probe(struct subchannel *sch)
return 0;
}
cdev = io_subchannel_create_ccwdev(sch);
- if (IS_ERR(cdev)) {
- rc = PTR_ERR(cdev);
+ if (IS_ERR(cdev))
goto out_err;
- }
rc = io_subchannel_recog(cdev, sch);
if (rc) {
spin_lock_irqsave(sch->lock, flags);
- sch_set_cdev(sch, NULL);
+ io_subchannel_recog_done(cdev);
spin_unlock_irqrestore(sch->lock, flags);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
- goto out_err;
}
return 0;
out_err:
kfree(sch->private);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
- return rc;
+out_schedule:
+ io_subchannel_schedule_removal(sch);
+ return 0;
}
static int
@@ -1275,10 +1354,7 @@ static void io_subchannel_verify(struct subchannel *sch)
static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
+ if (cio_update_schib(sch))
return 0;
if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
return 1;
@@ -1347,15 +1423,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
io_subchannel_verify(sch);
break;
case CHP_OFFLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
- if (!css_sch_is_valid(&sch->schib))
+ if (cio_update_schib(sch))
return -ENODEV;
io_subchannel_terminate_path(sch, mask);
break;
case CHP_ONLINE:
- if (stsch(sch->schid, &sch->schib))
- return -ENXIO;
+ if (cio_update_schib(sch))
+ return -ENODEV;
sch->lpm |= mask & sch->opm;
io_subchannel_verify(sch);
break;
@@ -1610,8 +1684,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
spin_lock_irqsave(sch->lock, flags);
/* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
+ sch->config.intparm = 0;
+ cio_commit_config(sch);
break;
case REPROBE:
ccw_device_trigger_reprobe(cdev);
@@ -1652,6 +1726,9 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
io_subchannel_init_fields(sch);
+ rc = cio_commit_config(sch);
+ if (rc)
+ return rc;
sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
@@ -1723,7 +1800,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
+ return (strcmp(bus_id, dev_name(dev)) == 0);
}
@@ -1806,6 +1883,8 @@ ccw_device_remove (struct device *dev)
"device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid,
cdev->private->dev_id.devno);
+ /* Give up reference obtained in ccw_device_set_online(). */
+ put_device(&cdev->dev);
}
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 104ed669db4..0f2e63ea48d 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -76,6 +76,7 @@ extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
void io_subchannel_recog_done(struct ccw_device *cdev);
+void io_subchannel_init_config(struct subchannel *sch);
int ccw_device_cancel_halt_clear(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 10bc03940fb..8df5eaafc5a 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -140,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
int ret;
sch = to_subchannel(cdev->dev.parent);
- ret = stsch(sch->schid, &sch->schib);
- if (ret || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (!sch->schib.pmcw.ena)
/* Not operational -> done. */
@@ -245,11 +244,13 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
* through ssch() and the path information is up to date.
*/
old_lpm = sch->lpm;
- stsch(sch->schid, &sch->schib);
- sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
/* Check since device may again have become not operational. */
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
state = DEV_STATE_NOT_OPER;
+ else
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
/* Force reprobe on all chpids. */
old_lpm = 0;
@@ -399,9 +400,6 @@ ccw_device_done(struct ccw_device *cdev, int state)
ccw_device_oper_notify(cdev);
}
wake_up(&cdev->private->wait_q);
-
- if (css_init_done && state != DEV_STATE_ONLINE)
- put_device (&cdev->dev);
}
static int cmp_pgid(struct pgid *p1, struct pgid *p2)
@@ -552,7 +550,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
/* Update schib - pom may have changed. */
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ cdev->private->flags.donotify = 0;
+ ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+ return;
+ }
/* Update lpm with verified path mask. */
sch->lpm = sch->vpm;
/* Repeat path verification? */
@@ -611,8 +613,6 @@ ccw_device_online(struct ccw_device *cdev)
(cdev->private->state != DEV_STATE_BOXED))
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- if (css_init_done && !get_device(&cdev->dev))
- return -ENODEV;
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
if (ret != 0) {
/* Couldn't enable the subchannel for i/o. Sick device. */
@@ -672,7 +672,7 @@ ccw_device_offline(struct ccw_device *cdev)
return 0;
}
sch = to_subchannel(cdev->dev.parent);
- if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return -ENODEV;
if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY;
@@ -750,7 +750,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
* Since we might not just be coming from an interrupt from the
* subchannel we have to update the schib.
*/
- stsch(sch->schid, &sch->schib);
+ if (cio_update_schib(sch)) {
+ ccw_device_verify_done(cdev, -ENODEV);
+ return;
+ }
if (scsw_actl(&sch->schib.scsw) != 0 ||
(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
@@ -1016,20 +1019,21 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
/* Update some values. */
- if (stsch(sch->schid, &sch->schib))
- return;
- if (!sch->schib.pmcw.dnv)
+ if (cio_update_schib(sch))
return;
/*
* The pim, pam, pom values may not be accurate, but they are the best
* we have before performing device selection :/
*/
sch->lpm = sch->schib.pmcw.pam & sch->opm;
- /* Re-set some bits in the pmcw that were lost. */
- sch->schib.pmcw.csense = 1;
- sch->schib.pmcw.ena = 0;
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1;
+ /*
+ * Use the initial configuration since we can't be shure that the old
+ * paths are valid.
+ */
+ io_subchannel_init_config(sch);
+ if (cio_commit_config(sch))
+ return;
+
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 86bc94eb607..fc5ca1dd52b 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -504,7 +504,7 @@ ccw_device_verify_start(struct ccw_device *cdev)
sch->vpm = 0;
/* Get current pam. */
- if (stsch(sch->schid, &sch->schib)) {
+ if (cio_update_schib(sch)) {
ccw_device_verify_done(cdev, -ENODEV);
return;
}
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 1b03c5423be..5814dbee241 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -56,7 +56,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- stsch (sch->schid, &sch->schib);
+ if (cio_update_schib(sch))
+ goto doverify;
CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
"not operational \n", __func__,
@@ -64,6 +65,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
+doverify:
cdev->private->flags.doverify = 1;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index e3ea1d5f281..42f2b09631b 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -10,10 +10,10 @@
#include <asm/page.h>
#include <asm/schid.h>
+#include <asm/debug.h>
#include "chsc.h"
#define QDIO_BUSY_BIT_PATIENCE 100 /* 100 microseconds */
-#define QDIO_BUSY_BIT_GIVE_UP 2000000 /* 2 seconds = eternity */
#define QDIO_INPUT_THRESHOLD 500 /* 500 microseconds */
/*
@@ -111,12 +111,12 @@ static inline int do_sqbs(u64 token, unsigned char state, int queue,
}
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count)
+ int *start, int *count, int ack)
{
register unsigned long _ccq asm ("0") = *count;
register unsigned long _token asm ("1") = token;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- unsigned long _state = 0;
+ unsigned long _state = (unsigned long)ack << 63;
asm volatile(
" .insn rrf,0xB99c0000,%1,%2,0,0"
@@ -133,7 +133,7 @@ static inline int do_eqbs(u64 token, unsigned char *state, int queue,
static inline int do_sqbs(u64 token, unsigned char state, int queue,
int *start, int *count) { return 0; }
static inline int do_eqbs(u64 token, unsigned char *state, int queue,
- int *start, int *count) { return 0; }
+ int *start, int *count, int ack) { return 0; }
#endif /* CONFIG_64BIT */
struct qdio_irq;
@@ -186,20 +186,14 @@ struct qdio_input_q {
/* input buffer acknowledgement flag */
int polling;
+ /* how much sbals are acknowledged with qebsm */
+ int ack_count;
+
/* last time of noticing incoming data */
u64 timestamp;
-
- /* lock for clearing the acknowledgement */
- spinlock_t lock;
};
struct qdio_output_q {
- /* failed siga-w attempts*/
- atomic_t busy_siga_counter;
-
- /* start time of busy condition */
- u64 timestamp;
-
/* PCIs are enabled for the queue */
int pci_out_enabled;
@@ -250,6 +244,7 @@ struct qdio_q {
struct qdio_irq *irq_ptr;
struct tasklet_struct tasklet;
+ spinlock_t lock;
/* error condition during a data transfer */
unsigned int qdio_error;
@@ -300,11 +295,13 @@ struct qdio_irq {
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];
+ debug_info_t *debug_area;
struct mutex setup_mutex;
};
/* helper functions */
#define queue_type(q) q->irq_ptr->qib.qfmt
+#define SCH_NO(q) (q->irq_ptr->schid.sch_no)
#define is_thinint_irq(irq) \
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
@@ -348,10 +345,13 @@ static inline unsigned long long get_usecs(void)
((bufnr + 1) & QDIO_MAX_BUFFERS_MASK)
#define add_buf(bufnr, inc) \
((bufnr + inc) & QDIO_MAX_BUFFERS_MASK)
+#define sub_buf(bufnr, dec) \
+ ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK)
/* prototypes for thin interrupt */
void qdio_sync_after_thinint(struct qdio_q *q);
-int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state);
+int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state,
+ int auto_ack);
void qdio_check_outbound_after_thinint(struct qdio_q *q);
int qdio_inbound_q_moved(struct qdio_q *q);
void qdio_kick_inbound_handler(struct qdio_q *q);
@@ -378,10 +378,15 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs,
int nr_output_qs);
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr);
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data);
int qdio_setup_irq(struct qdio_initialize *init_data);
void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_release_memory(struct qdio_irq *irq_ptr);
+int qdio_setup_create_sysfs(struct ccw_device *cdev);
+void qdio_setup_destroy_sysfs(struct ccw_device *cdev);
int qdio_setup_init(void);
void qdio_setup_exit(void);
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index f05590355be..f8a3b6967f6 100644
--- a/drivers/s390/cio/qdio_debug.c
+++ b/drivers/s390/cio/qdio_debug.c
@@ -14,7 +14,7 @@
#include "qdio.h"
debug_info_t *qdio_dbf_setup;
-debug_info_t *qdio_dbf_trace;
+debug_info_t *qdio_dbf_error;
static struct dentry *debugfs_root;
#define MAX_DEBUGFS_QUEUES 32
@@ -22,59 +22,33 @@ static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL };
static DEFINE_MUTEX(debugfs_mutex);
#define QDIO_DEBUGFS_NAME_LEN 40
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data)
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr)
{
- char dbf_text[20];
-
- sprintf(dbf_text, "qfmt:%x", init_data->q_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8);
- sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *));
- sprintf(dbf_text, "niq:%4x", init_data->no_input_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "noq:%4x", init_data->no_output_qs);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long));
- QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *));
- QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *));
-}
-
-static void qdio_unregister_dbf_views(void)
-{
- if (qdio_dbf_setup)
- debug_unregister(qdio_dbf_setup);
- if (qdio_dbf_trace)
- debug_unregister(qdio_dbf_trace);
-}
-
-static int qdio_register_dbf_views(void)
-{
- qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES,
- QDIO_DBF_SETUP_NR_AREAS,
- QDIO_DBF_SETUP_LEN);
- if (!qdio_dbf_setup)
- goto oom;
- debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL);
-
- qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES,
- QDIO_DBF_TRACE_NR_AREAS,
- QDIO_DBF_TRACE_LEN);
- if (!qdio_dbf_trace)
- goto oom;
- debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view);
- debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL);
- return 0;
-oom:
- qdio_unregister_dbf_views();
- return -ENOMEM;
+ char text[20];
+
+ DBF_EVENT("qfmt:%1d", init_data->q_format);
+ DBF_HEX(init_data->adapter_name, 8);
+ DBF_EVENT("qpff%4x", init_data->qib_param_field_format);
+ DBF_HEX(&init_data->qib_param_field, sizeof(void *));
+ DBF_HEX(&init_data->input_slib_elements, sizeof(void *));
+ DBF_HEX(&init_data->output_slib_elements, sizeof(void *));
+ DBF_EVENT("niq:%1d noq:%1d", init_data->no_input_qs,
+ init_data->no_output_qs);
+ DBF_HEX(&init_data->input_handler, sizeof(void *));
+ DBF_HEX(&init_data->output_handler, sizeof(void *));
+ DBF_HEX(&init_data->int_parm, sizeof(long));
+ DBF_HEX(&init_data->flags, sizeof(long));
+ DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
+ DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
+ DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
+
+ /* allocate trace view for the interface */
+ snprintf(text, 20, "qdio_%s", dev_name(&init_data->cdev->dev));
+ irq_ptr->debug_area = debug_register(text, 2, 1, 16);
+ debug_register_view(irq_ptr->debug_area, &debug_hex_ascii_view);
+ debug_set_level(irq_ptr->debug_area, DBF_WARN);
+ DBF_DEV_EVENT(DBF_ERR, irq_ptr, "dbf created");
}
static int qstat_show(struct seq_file *m, void *v)
@@ -86,16 +60,18 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q)
return 0;
- seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci);
+ seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci);
seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used));
seq_printf(m, "ftc: %d\n", q->first_to_check);
seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc);
seq_printf(m, "polling: %d\n", q->u.in.polling);
+ seq_printf(m, "ack count: %d\n", q->u.in.ack_count);
seq_printf(m, "slsb buffer states:\n");
+ seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n");
qdio_siga_sync_q(q);
for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) {
- get_buf_state(q, i, &state);
+ get_buf_state(q, i, &state, 0);
switch (state) {
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_OUTPUT_NOT_INIT:
@@ -127,6 +103,7 @@ static int qstat_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
}
seq_printf(m, "\n");
+ seq_printf(m, "|64 |72 |80 |88 |96 |104 |112 | 127|\n");
return 0;
}
@@ -223,11 +200,24 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd
int __init qdio_debug_init(void)
{
debugfs_root = debugfs_create_dir("qdio_queues", NULL);
- return qdio_register_dbf_views();
+
+ qdio_dbf_setup = debug_register("qdio_setup", 16, 1, 16);
+ debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_setup, DBF_INFO);
+ DBF_EVENT("dbf created\n");
+
+ qdio_dbf_error = debug_register("qdio_error", 4, 1, 16);
+ debug_register_view(qdio_dbf_error, &debug_hex_ascii_view);
+ debug_set_level(qdio_dbf_error, DBF_INFO);
+ DBF_ERROR("dbf created\n");
+ return 0;
}
void qdio_debug_exit(void)
{
debugfs_remove(debugfs_root);
- qdio_unregister_dbf_views();
+ if (qdio_dbf_setup)
+ debug_unregister(qdio_dbf_setup);
+ if (qdio_dbf_error)
+ debug_unregister(qdio_dbf_error);
}
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index 5a4d85b829a..5d70bd162ae 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -12,80 +12,72 @@
#include <asm/qdio.h>
#include "qdio.h"
-#define QDIO_DBF_HEX(ex, name, level, addr, len) \
+/* that gives us 15 characters in the text event views */
+#define QDIO_DBF_LEN 16
+
+extern debug_info_t *qdio_dbf_setup;
+extern debug_info_t *qdio_dbf_error;
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+ return (level <= dbf_grp->level);
+}
+
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 6 /* informational */
+
+#undef DBF_EVENT
+#undef DBF_ERROR
+#undef DBF_DEV_EVENT
+
+#define DBF_EVENT(text...) \
do { \
- if (ex) \
- debug_exception(qdio_dbf_##name, level, (void *)(addr), len); \
- else \
- debug_event(qdio_dbf_##name, level, (void *)(addr), len); \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
} while (0)
-#define QDIO_DBF_TEXT(ex, name, level, text) \
+
+#define DBF_HEX(addr, len) \
do { \
- if (ex) \
- debug_text_exception(qdio_dbf_##name, level, text); \
- else \
- debug_text_event(qdio_dbf_##name, level, text); \
+ debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
} while (0)
-#define QDIO_DBF_HEX0(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 0, addr, len)
-#define QDIO_DBF_HEX1(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 1, addr, len)
-#define QDIO_DBF_HEX2(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 2, addr, len)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_HEX3(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 3, addr, len)
-#define QDIO_DBF_HEX4(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 4, addr, len)
-#define QDIO_DBF_HEX5(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 5, addr, len)
-#define QDIO_DBF_HEX6(ex, name, addr, len) QDIO_DBF_HEX(ex, name, 6, addr, len)
-#else
-#define QDIO_DBF_HEX3(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX4(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX5(ex, name, addr, len) do {} while (0)
-#define QDIO_DBF_HEX6(ex, name, addr, len) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
-
-#define QDIO_DBF_TEXT0(ex, name, text) QDIO_DBF_TEXT(ex, name, 0, text)
-#define QDIO_DBF_TEXT1(ex, name, text) QDIO_DBF_TEXT(ex, name, 1, text)
-#define QDIO_DBF_TEXT2(ex, name, text) QDIO_DBF_TEXT(ex, name, 2, text)
-
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TEXT3(ex, name, text) QDIO_DBF_TEXT(ex, name, 3, text)
-#define QDIO_DBF_TEXT4(ex, name, text) QDIO_DBF_TEXT(ex, name, 4, text)
-#define QDIO_DBF_TEXT5(ex, name, text) QDIO_DBF_TEXT(ex, name, 5, text)
-#define QDIO_DBF_TEXT6(ex, name, text) QDIO_DBF_TEXT(ex, name, 6, text)
-#else
-#define QDIO_DBF_TEXT3(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT4(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT5(ex, name, text) do {} while (0)
-#define QDIO_DBF_TEXT6(ex, name, text) do {} while (0)
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_ERROR(text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
+ } while (0)
-/* s390dbf views */
-#define QDIO_DBF_SETUP_LEN 8
-#define QDIO_DBF_SETUP_PAGES 8
-#define QDIO_DBF_SETUP_NR_AREAS 1
+#define DBF_ERROR_HEX(addr, len) \
+ do { \
+ debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
+ } while (0)
-#define QDIO_DBF_TRACE_LEN 8
-#define QDIO_DBF_TRACE_NR_AREAS 2
-#ifdef CONFIG_QDIO_DEBUG
-#define QDIO_DBF_TRACE_PAGES 32
-#define QDIO_DBF_SETUP_LEVEL 6
-#define QDIO_DBF_TRACE_LEVEL 4
-#else /* !CONFIG_QDIO_DEBUG */
-#define QDIO_DBF_TRACE_PAGES 8
-#define QDIO_DBF_SETUP_LEVEL 2
-#define QDIO_DBF_TRACE_LEVEL 2
-#endif /* CONFIG_QDIO_DEBUG */
+#define DBF_DEV_EVENT(level, device, text...) \
+ do { \
+ char debug_buffer[QDIO_DBF_LEN]; \
+ if (qdio_dbf_passes(device->debug_area, level)) { \
+ snprintf(debug_buffer, QDIO_DBF_LEN, text); \
+ debug_text_event(device->debug_area, level, debug_buffer); \
+ } \
+ } while (0)
-extern debug_info_t *qdio_dbf_setup;
-extern debug_info_t *qdio_dbf_trace;
+#define DBF_DEV_HEX(level, device, addr, len) \
+ do { \
+ debug_event(device->debug_area, level, (void*)(addr), len); \
+ } while (0)
-void qdio_allocate_do_dbf(struct qdio_initialize *init_data);
-void debug_print_bstat(struct qdio_q *q);
+void qdio_allocate_dbf(struct qdio_initialize *init_data,
+ struct qdio_irq *irq_ptr);
void qdio_setup_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr,
struct ccw_device *cdev);
int qdio_debug_init(void);
void qdio_debug_exit(void);
+
#endif
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index 7c865915199..744f928a59e 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -74,7 +74,7 @@ static inline int do_siga_input(struct subchannel_id schid, unsigned int mask)
* Note: For IQDC unicast queues only the highest priority queue is processed.
*/
static inline int do_siga_output(unsigned long schid, unsigned long mask,
- u32 *bb, unsigned int fc)
+ unsigned int *bb, unsigned int fc)
{
register unsigned long __fc asm("0") = fc;
register unsigned long __schid asm("1") = schid;
@@ -95,8 +95,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
{
- char dbf_text[15];
-
/* all done or next buffer state different */
if (ccq == 0 || ccq == 32)
return 0;
@@ -104,8 +102,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
if (ccq == 96 || ccq == 97)
return 1;
/* notify devices immediately */
- sprintf(dbf_text, "%d", ccq);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
return -EIO;
}
@@ -115,41 +112,45 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
* @state: state of the extracted buffers
* @start: buffer number to start at
* @count: count of buffers to examine
+ * @auto_ack: automatically acknowledge buffers
*
* Returns the number of successfull extracted equal buffer states.
* Stops processing if a state is different from the last buffers state.
*/
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
- int start, int count)
+ int start, int count, int auto_ack)
{
unsigned int ccq = 0;
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
again:
- ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
+ ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
+ auto_ack);
rc = qdio_check_ccq(q, ccq);
/* At least one buffer was processed, return and extract the remaining
* buffers later.
*/
- if ((ccq == 96) && (count != tmp_count))
+ if ((ccq == 96) && (count != tmp_count)) {
+ qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete);
return (count - tmp_count);
+ }
+
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "eqAGAIN");
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT2(1, trace, "eqberr");
- sprintf(dbf_text, "%2x,%2x,%d,%d", count, tmp_count, ccq, nr);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -176,9 +177,12 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
int tmp_count = count, tmp_start = start;
int nr = q->nr;
int rc;
- char dbf_text[15];
+
+ if (!count)
+ return 0;
BUG_ON(!q->irq_ptr->sch_token);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_all);
if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs;
@@ -186,16 +190,13 @@ again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
rc = qdio_check_ccq(q, ccq);
if (rc == 1) {
- QDIO_DBF_TEXT5(1, trace, "sqAGAIN");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
+ qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete);
goto again;
}
if (rc < 0) {
- QDIO_DBF_TEXT3(1, trace, "sqberr");
- sprintf(dbf_text, "%2x,%2x", count, tmp_count);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- sprintf(dbf_text, "%d,%d", ccq, nr);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
-
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
q->handler(q->irq_ptr->cdev,
QDIO_ERROR_ACTIVATE_CHECK_CONDITION,
0, -1, -1, q->irq_ptr->int_parm);
@@ -207,7 +208,8 @@ again:
/* returns number of examined buffers and their common state in *state */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state, unsigned int count)
+ unsigned char *state, unsigned int count,
+ int auto_ack)
{
unsigned char __state = 0;
int i;
@@ -216,7 +218,7 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
BUG_ON(count > QDIO_MAX_BUFFERS_PER_Q);
if (is_qebsm(q))
- return qdio_do_eqbs(q, state, bufnr, count);
+ return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
for (i = 0; i < count; i++) {
if (!__state)
@@ -230,9 +232,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
}
inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
- unsigned char *state)
+ unsigned char *state, int auto_ack)
{
- return get_buf_states(q, bufnr, state, 1);
+ return get_buf_states(q, bufnr, state, 1, auto_ack);
}
/* wrap-around safe setting of slsb states, returns number of changed buffers */
@@ -282,14 +284,12 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output,
if (!need_siga_sync(q))
return 0;
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_sync);
cc = do_siga_sync(q->irq_ptr->schid, output, input);
- if (cc) {
- QDIO_DBF_TEXT4(0, trace, "sigasync");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
- }
+ if (cc)
+ DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc);
return cc;
}
@@ -311,50 +311,37 @@ static inline int qdio_siga_sync_all(struct qdio_q *q)
return qdio_siga_sync(q, ~0U, ~0U);
}
-static inline int qdio_do_siga_output(struct qdio_q *q, unsigned int *busy_bit)
+static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit)
{
- unsigned int fc = 0;
unsigned long schid;
+ unsigned int fc = 0;
+ u64 start_time = 0;
+ int cc;
- if (q->u.out.use_enh_siga) {
+ if (q->u.out.use_enh_siga)
fc = 3;
- }
- if (!is_qebsm(q))
- schid = *((u32 *)&q->irq_ptr->schid);
- else {
+
+ if (is_qebsm(q)) {
schid = q->irq_ptr->sch_token;
fc |= 0x80;
}
- return do_siga_output(schid, q->mask, busy_bit, fc);
-}
-
-static int qdio_siga_output(struct qdio_q *q)
-{
- int cc;
- u32 busy_bit;
- u64 start_time = 0;
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "sigaout");
- QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
+ else
+ schid = *((u32 *)&q->irq_ptr->schid);
- qdio_perf_stat_inc(&perf_stats.siga_out);
again:
- cc = qdio_do_siga_output(q, &busy_bit);
- if (queue_type(q) == QDIO_IQDIO_QFMT && cc == 2 && busy_bit) {
- sprintf(dbf_text, "bb%4x%2x", q->irq_ptr->schid.sch_no, q->nr);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
+ cc = do_siga_output(schid, q->mask, busy_bit, fc);
- if (!start_time)
+ /* hipersocket busy condition */
+ if (*busy_bit) {
+ WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2);
+
+ if (!start_time) {
start_time = get_usecs();
- else if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
+ goto again;
+ }
+ if ((get_usecs() - start_time) < QDIO_BUSY_BIT_PATIENCE)
goto again;
}
-
- if (cc == 2 && busy_bit)
- cc |= QDIO_ERROR_SIGA_BUSY;
- if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
return cc;
}
@@ -362,14 +349,12 @@ static inline int qdio_siga_input(struct qdio_q *q)
{
int cc;
- QDIO_DBF_TEXT4(0, trace, "sigain");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_in);
cc = do_siga_input(q->irq_ptr->schid, q->mask);
if (cc)
- QDIO_DBF_HEX3(0, trace, &cc, sizeof(int *));
+ DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc);
return cc;
}
@@ -387,35 +372,91 @@ void qdio_sync_after_thinint(struct qdio_q *q)
inline void qdio_stop_polling(struct qdio_q *q)
{
- spin_lock_bh(&q->u.in.lock);
- if (!q->u.in.polling) {
- spin_unlock_bh(&q->u.in.lock);
+ if (!q->u.in.polling)
return;
- }
+
q->u.in.polling = 0;
qdio_perf_stat_inc(&perf_stats.debug_stop_polling);
/* show the card that we are not polling anymore */
- set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
- spin_unlock_bh(&q->u.in.lock);
+ if (is_qebsm(q)) {
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = 0;
+ } else
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
}
-static void announce_buffer_error(struct qdio_q *q)
+static void announce_buffer_error(struct qdio_q *q, int count)
{
- char dbf_text[15];
+ q->qdio_error |= QDIO_ERROR_SLSB_STATE;
+
+ /* special handling for no target buffer empty */
+ if ((!q->is_input_q &&
+ (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) {
+ qdio_perf_stat_inc(&perf_stats.outbound_target_full);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%3d",
+ q->first_to_check);
+ return;
+ }
- if (q->is_input_q)
- QDIO_DBF_TEXT3(1, trace, "inperr");
- else
- QDIO_DBF_TEXT3(0, trace, "outperr");
+ DBF_ERROR("%4x BUF ERROR", SCH_NO(q));
+ DBF_ERROR((q->is_input_q) ? "IN:%2d" : "OUT:%2d", q->nr);
+ DBF_ERROR("FTC:%3d C:%3d", q->first_to_check, count);
+ DBF_ERROR("F14:%2x F15:%2x",
+ q->sbal[q->first_to_check]->element[14].flags & 0xff,
+ q->sbal[q->first_to_check]->element[15].flags & 0xff);
+}
+
+static inline void inbound_primed(struct qdio_q *q, int count)
+{
+ int new;
+
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %3d", count);
+
+ /* for QEBSM the ACK was already set by EQBS */
+ if (is_qebsm(q)) {
+ if (!q->u.in.polling) {
+ q->u.in.polling = 1;
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /* delete the previous ACK's */
+ set_buf_states(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT,
+ q->u.in.ack_count);
+ q->u.in.ack_count = count;
+ q->last_move_ftc = q->first_to_check;
+ return;
+ }
+
+ /*
+ * ACK the newest buffer. The ACK will be removed in qdio_stop_polling
+ * or by the next inbound run.
+ */
+ new = add_buf(q->first_to_check, count - 1);
+ if (q->u.in.polling) {
+ /* reset the previous ACK but first set the new one */
+ set_buf_state(q, new, SLSB_P_INPUT_ACK);
+ set_buf_state(q, q->last_move_ftc, SLSB_P_INPUT_NOT_INIT);
+ }
+ else {
+ q->u.in.polling = 1;
+ set_buf_state(q, q->first_to_check, SLSB_P_INPUT_ACK);
+ }
- sprintf(dbf_text, "%x-%x-%x", q->first_to_check,
- q->sbal[q->first_to_check]->element[14].flags,
- q->sbal[q->first_to_check]->element[15].flags);
- QDIO_DBF_TEXT3(1, trace, dbf_text);
- QDIO_DBF_HEX2(1, trace, q->sbal[q->first_to_check], 256);
+ q->last_move_ftc = new;
+ count--;
+ if (!count)
+ return;
- q->qdio_error = QDIO_ERROR_SLSB_STATE;
+ /*
+ * Need to change all PRIMED buffers to NOT_INIT, otherwise
+ * we're loosing initiative in the thinint code.
+ */
+ set_buf_states(q, next_buf(q->first_to_check), SLSB_P_INPUT_NOT_INIT,
+ count);
}
static int get_inbound_buffer_frontier(struct qdio_q *q)
@@ -424,13 +465,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
unsigned char state;
/*
- * If we still poll don't update last_move_ftc, keep the
- * previously ACK buffer there.
- */
- if (!q->u.in.polling)
- q->last_move_ftc = q->first_to_check;
-
- /*
* Don't check 128 buffers, as otherwise qdio_inbound_q_moved
* would return 0.
*/
@@ -450,34 +484,13 @@ check_next:
if (q->first_to_check == stop)
goto out;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 1);
if (!count)
goto out;
switch (state) {
case SLSB_P_INPUT_PRIMED:
- QDIO_DBF_TEXT5(0, trace, "inptprim");
-
- /*
- * Only ACK the first buffer. The ACK will be removed in
- * qdio_stop_polling.
- */
- if (q->u.in.polling)
- state = SLSB_P_INPUT_NOT_INIT;
- else {
- q->u.in.polling = 1;
- state = SLSB_P_INPUT_ACK;
- }
- set_buf_state(q, q->first_to_check, state);
-
- /*
- * Need to change all PRIMED buffers to NOT_INIT, otherwise
- * we're loosing initiative in the thinint code.
- */
- if (count > 1)
- set_buf_states(q, next_buf(q->first_to_check),
- SLSB_P_INPUT_NOT_INIT, count - 1);
-
+ inbound_primed(q, count);
/*
* No siga-sync needed for non-qebsm here, as the inbound queue
* will be synced on the next siga-r, resp.
@@ -487,7 +500,7 @@ check_next:
atomic_sub(count, &q->nr_buf_used);
goto check_next;
case SLSB_P_INPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
@@ -495,13 +508,12 @@ check_next:
case SLSB_CU_INPUT_EMPTY:
case SLSB_P_INPUT_NOT_INIT:
case SLSB_P_INPUT_ACK:
- QDIO_DBF_TEXT5(0, trace, "inpnipro");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop");
break;
default:
BUG();
}
out:
- QDIO_DBF_HEX4(0, trace, &q->first_to_check, sizeof(int));
return q->first_to_check;
}
@@ -515,8 +527,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
if (!need_siga_sync(q) && !pci_out_supported(q))
q->u.in.timestamp = get_usecs();
- QDIO_DBF_TEXT4(0, trace, "inhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved");
return 1;
} else
return 0;
@@ -524,10 +535,7 @@ int qdio_inbound_q_moved(struct qdio_q *q)
static int qdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
@@ -538,7 +546,7 @@ static int qdio_inbound_q_done(struct qdio_q *q)
*/
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* we got something to do */
return 0;
@@ -552,20 +560,12 @@ static int qdio_inbound_q_done(struct qdio_q *q)
* has (probably) not moved (see qdio_inbound_processing).
*/
if (get_usecs() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisdon");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d",
+ q->first_to_check);
return 1;
} else {
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "inqisntd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
- sprintf(dbf_text, "pf%02x", q->first_to_check);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d",
+ q->first_to_check);
return 0;
}
}
@@ -573,9 +573,6 @@ static int qdio_inbound_q_done(struct qdio_q *q)
void qdio_kick_inbound_handler(struct qdio_q *q)
{
int count, start, end;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
qdio_perf_stat_inc(&perf_stats.inbound_handler);
@@ -586,10 +583,7 @@ void qdio_kick_inbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -655,14 +649,14 @@ check_next:
if (q->first_to_check == stop)
return q->first_to_check;
- count = get_buf_states(q, q->first_to_check, &state, count);
+ count = get_buf_states(q, q->first_to_check, &state, count, 0);
if (!count)
return q->first_to_check;
switch (state) {
case SLSB_P_OUTPUT_EMPTY:
/* the adapter got it */
- QDIO_DBF_TEXT5(0, trace, "outpempt");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out empty:%1d %3d", q->nr, count);
atomic_sub(count, &q->nr_buf_used);
q->first_to_check = add_buf(q->first_to_check, count);
@@ -674,14 +668,14 @@ check_next:
break;
goto check_next;
case SLSB_P_OUTPUT_ERROR:
- announce_buffer_error(q);
+ announce_buffer_error(q, count);
/* process the buffer, the upper layer will take care of it */
q->first_to_check = add_buf(q->first_to_check, count);
atomic_sub(count, &q->nr_buf_used);
break;
case SLSB_CU_OUTPUT_PRIMED:
/* the adapter has not fetched the output yet */
- QDIO_DBF_TEXT5(0, trace, "outpprim");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out primed:%1d", q->nr);
break;
case SLSB_P_OUTPUT_NOT_INIT:
case SLSB_P_OUTPUT_HALTED:
@@ -706,99 +700,48 @@ static inline int qdio_outbound_q_moved(struct qdio_q *q)
if ((bufnr != q->last_move_ftc) || q->qdio_error) {
q->last_move_ftc = bufnr;
- QDIO_DBF_TEXT4(0, trace, "oqhasmvd");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
return 1;
} else
return 0;
}
-/*
- * VM could present us cc=2 and busy bit set on SIGA-write
- * during reconfiguration of their Guest LAN (only in iqdio mode,
- * otherwise qdio is asynchronous and cc=2 and busy bit there will take
- * the queues down immediately).
- *
- * Therefore qdio_siga_output will try for a short time constantly,
- * if such a condition occurs. If it doesn't change, it will
- * increase the busy_siga_counter and save the timestamp, and
- * schedule the queue for later processing. qdio_outbound_processing
- * will check out the counter. If non-zero, it will call qdio_kick_outbound_q
- * as often as the value of the counter. This will attempt further SIGA
- * instructions. For each successful SIGA, the counter is
- * decreased, for failing SIGAs the counter remains the same, after
- * all. After some time of no movement, qdio_kick_outbound_q will
- * finally fail and reflect corresponding error codes to call
- * the upper layer module and have it take the queues down.
- *
- * Note that this is a change from the original HiperSockets design
- * (saying cc=2 and busy bit means take the queues down), but in
- * these days Guest LAN didn't exist... excessive cc=2 with busy bit
- * conditions will still take the queues down, but the threshold is
- * higher due to the Guest LAN environment.
- *
- * Called from outbound tasklet and do_QDIO handler.
- */
static void qdio_kick_outbound_q(struct qdio_q *q)
{
- int rc;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "kickoutq");
- QDIO_DBF_HEX5(0, trace, &q, sizeof(void *));
-#endif /* CONFIG_QDIO_DEBUG */
+ unsigned int busy_bit;
+ int cc;
if (!need_siga_out(q))
return;
- rc = qdio_siga_output(q);
- switch (rc) {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
+ qdio_perf_stat_inc(&perf_stats.siga_out);
+
+ cc = qdio_siga_output(q, &busy_bit);
+ switch (cc) {
case 0:
- /* TODO: improve error handling for CC=0 case */
-#ifdef CONFIG_QDIO_DEBUG
- if (q->u.out.timestamp) {
- QDIO_DBF_TEXT3(0, trace, "cc2reslv");
- sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no,
- q->nr,
- atomic_read(&q->u.out.busy_siga_counter));
- QDIO_DBF_TEXT3(0, trace, dbf_text);
- }
-#endif /* CONFIG_QDIO_DEBUG */
- /* went smooth this time, reset timestamp */
- q->u.out.timestamp = 0;
break;
- /* cc=2 and busy bit */
- case (2 | QDIO_ERROR_SIGA_BUSY):
- atomic_inc(&q->u.out.busy_siga_counter);
-
- /* if the last siga was successful, save timestamp here */
- if (!q->u.out.timestamp)
- q->u.out.timestamp = get_usecs();
-
- /* if we're in time, don't touch qdio_error */
- if (get_usecs() - q->u.out.timestamp < QDIO_BUSY_BIT_GIVE_UP) {
- tasklet_schedule(&q->tasklet);
- break;
+ case 2:
+ if (busy_bit) {
+ DBF_ERROR("%4x cc2 REP:%1d", SCH_NO(q), q->nr);
+ q->qdio_error = cc | QDIO_ERROR_SIGA_BUSY;
+ } else {
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w cc2:%1d",
+ q->nr);
+ q->qdio_error = cc;
}
- QDIO_DBF_TEXT2(0, trace, "cc2REPRT");
-#ifdef CONFIG_QDIO_DEBUG
- sprintf(dbf_text, "%4x%2x%2x", q->irq_ptr->schid.sch_no, q->nr,
- atomic_read(&q->u.out.busy_siga_counter));
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
- default:
- /* for plain cc=1, 2 or 3 */
- q->qdio_error = rc;
+ break;
+ case 1:
+ case 3:
+ DBF_ERROR("%4x SIGA-W:%1d", SCH_NO(q), cc);
+ q->qdio_error = cc;
+ break;
}
}
static void qdio_kick_outbound_handler(struct qdio_q *q)
{
int start, end, count;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-#endif
start = q->first_to_kick;
end = q->last_move_ftc;
@@ -807,13 +750,8 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
else
count = end + QDIO_MAX_BUFFERS_PER_Q - start;
-#ifdef CONFIG_QDIO_DEBUG
- QDIO_DBF_TEXT4(0, trace, "kickouth");
- QDIO_DBF_HEX4(0, trace, &q, sizeof(void *));
-
- sprintf(dbf_text, "s=%2xc=%2x", start, count);
- QDIO_DBF_TEXT4(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kickouth: %1d", q->nr);
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "s:%3d c:%3d", start, count);
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
@@ -828,22 +766,18 @@ static void qdio_kick_outbound_handler(struct qdio_q *q)
static void __qdio_outbound_processing(struct qdio_q *q)
{
- int siga_attempts;
+ unsigned long flags;
qdio_perf_stat_inc(&perf_stats.tasklet_outbound);
-
- /* see comment in qdio_kick_outbound_q */
- siga_attempts = atomic_read(&q->u.out.busy_siga_counter);
- while (siga_attempts--) {
- atomic_dec(&q->u.out.busy_siga_counter);
- qdio_kick_outbound_q(q);
- }
+ spin_lock_irqsave(&q->lock, flags);
BUG_ON(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q))
qdio_kick_outbound_handler(q);
+ spin_unlock_irqrestore(&q->lock, flags);
+
if (queue_type(q) == QDIO_ZFCP_QFMT) {
if (!pci_out_supported(q) && !qdio_outbound_q_done(q))
tasklet_schedule(&q->tasklet);
@@ -908,27 +842,18 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q)
static inline void qdio_set_state(struct qdio_irq *irq_ptr,
enum qdio_irq_states state)
{
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[15];
-
- QDIO_DBF_TEXT5(0, trace, "newstate");
- sprintf(dbf_text, "%4x%4x", irq_ptr->schid.sch_no, state);
- QDIO_DBF_TEXT5(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "newstate: %1d", state);
irq_ptr->state = state;
mb();
}
-static void qdio_irq_check_sense(struct subchannel_id schid, struct irb *irb)
+static void qdio_irq_check_sense(struct qdio_irq *irq_ptr, struct irb *irb)
{
- char dbf_text[15];
-
if (irb->esw.esw0.erw.cons) {
- sprintf(dbf_text, "sens%4x", schid.sch_no);
- QDIO_DBF_TEXT2(1, trace, dbf_text);
- QDIO_DBF_HEX0(0, trace, irb, 64);
- QDIO_DBF_HEX0(0, trace, irb->ecw, 64);
+ DBF_ERROR("%4x sense:", irq_ptr->schid.sch_no);
+ DBF_ERROR_HEX(irb, 64);
+ DBF_ERROR_HEX(irb->ecw, 64);
}
}
@@ -962,14 +887,10 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
struct qdio_q *q;
- char dbf_text[15];
- QDIO_DBF_TEXT2(1, trace, "ick2");
- sprintf(dbf_text, "%s", dev_name(&cdev->dev));
- QDIO_DBF_TEXT2(1, trace, dbf_text);
- QDIO_DBF_HEX2(0, trace, &intparm, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
+ DBF_ERROR("%4x ACT CHECK", irq_ptr->schid.sch_no);
+ DBF_ERROR("intp :%lx", intparm);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
if (irq_ptr->nr_input_qs) {
q = irq_ptr->input_qs[0];
@@ -1022,28 +943,29 @@ static void qdio_int_error(struct ccw_device *cdev)
}
static int qdio_establish_check_errors(struct ccw_device *cdev, int cstat,
- int dstat)
+ int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
if (cstat || (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
- QDIO_DBF_TEXT2(1, setup, "eq:ckcon");
+ DBF_ERROR("EQ:ck con");
goto error;
}
if (!(dstat & DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:no de");
+ DBF_ERROR("EQ:no dev");
goto error;
}
if (dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- QDIO_DBF_TEXT2(1, setup, "eq:badio");
+ DBF_ERROR("EQ: bad io");
goto error;
}
return 0;
error:
- QDIO_DBF_HEX2(0, trace, &cstat, sizeof(int));
- QDIO_DBF_HEX2(0, trace, &dstat, sizeof(int));
+ DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
+ DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
+
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
@@ -1052,12 +974,8 @@ static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
int dstat)
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- char dbf_text[15];
-
- sprintf(dbf_text, "qehi%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_TEXT0(0, trace, dbf_text);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
if (!qdio_establish_check_errors(cdev, cstat, dstat))
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
}
@@ -1068,25 +986,21 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
{
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int cstat, dstat;
- char dbf_text[15];
qdio_perf_stat_inc(&perf_stats.qdio_int);
if (!intparm || !irq_ptr) {
- sprintf(dbf_text, "qihd%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
return;
}
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
- sprintf(dbf_text, "ierr%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO error", irq_ptr->schid.sch_no);
return;
case -ETIMEDOUT:
- sprintf(dbf_text, "qtoh%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x IO timeout", irq_ptr->schid.sch_no);
qdio_int_error(cdev);
return;
default:
@@ -1094,7 +1008,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
return;
}
}
- qdio_irq_check_sense(irq_ptr->schid, irb);
+ qdio_irq_check_sense(irq_ptr, irb);
cstat = irb->scsw.cmd.cstat;
dstat = irb->scsw.cmd.dstat;
@@ -1129,23 +1043,20 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
/**
* qdio_get_ssqd_desc - get qdio subchannel description
* @cdev: ccw device to get description for
+ * @data: where to store the ssqd
*
- * Returns a pointer to the saved qdio subchannel description,
- * or NULL for not setup qdio devices.
+ * Returns 0 or an error code. The results of the chsc are stored in the
+ * specified structure.
*/
-struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev)
+int qdio_get_ssqd_desc(struct ccw_device *cdev,
+ struct qdio_ssqd_desc *data)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- return NULL;
+ if (!cdev || !cdev->private)
+ return -EINVAL;
- return &irq_ptr->ssqd_desc;
+ DBF_EVENT("get ssqd:%4x", cdev->private->schid.sch_no);
+ return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data);
}
EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
@@ -1159,14 +1070,9 @@ EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc);
*/
int qdio_cleanup(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int rc;
- sprintf(dbf_text, "qcln%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
@@ -1199,18 +1105,15 @@ static void qdio_shutdown_queues(struct ccw_device *cdev)
*/
int qdio_shutdown(struct ccw_device *cdev, int how)
{
- struct qdio_irq *irq_ptr;
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int rc;
unsigned long flags;
- char dbf_text[15];
- sprintf(dbf_text, "qshu%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
+ DBF_EVENT("qshutdown:%4x", cdev->private->schid.sch_no);
+
mutex_lock(&irq_ptr->setup_mutex);
/*
* Subchannel was already shot down. We cannot prevent being called
@@ -1234,10 +1137,8 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
/* default behaviour is halt */
rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
if (rc) {
- sprintf(dbf_text, "sher%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- sprintf(dbf_text, "rc=%d", rc);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_ERROR("%4x SHUTD ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4d", rc);
goto no_cleanup;
}
@@ -1271,17 +1172,18 @@ EXPORT_SYMBOL_GPL(qdio_shutdown);
*/
int qdio_free(struct ccw_device *cdev)
{
- struct qdio_irq *irq_ptr;
- char dbf_text[15];
-
- sprintf(dbf_text, "qfre%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ struct qdio_irq *irq_ptr = cdev->private->qdio_data;
- irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
return -ENODEV;
+ DBF_EVENT("qfree:%4x", cdev->private->schid.sch_no);
mutex_lock(&irq_ptr->setup_mutex);
+
+ if (irq_ptr->debug_area != NULL) {
+ debug_unregister(irq_ptr->debug_area);
+ irq_ptr->debug_area = NULL;
+ }
cdev->private->qdio_data = NULL;
mutex_unlock(&irq_ptr->setup_mutex);
@@ -1300,10 +1202,6 @@ EXPORT_SYMBOL_GPL(qdio_free);
int qdio_initialize(struct qdio_initialize *init_data)
{
int rc;
- char dbf_text[15];
-
- sprintf(dbf_text, "qini%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
rc = qdio_allocate(init_data);
if (rc)
@@ -1323,10 +1221,8 @@ EXPORT_SYMBOL_GPL(qdio_initialize);
int qdio_allocate(struct qdio_initialize *init_data)
{
struct qdio_irq *irq_ptr;
- char dbf_text[15];
- sprintf(dbf_text, "qalc%4x", init_data->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qallocate:%4x", init_data->cdev->private->schid.sch_no);
if ((init_data->no_input_qs && !init_data->input_handler) ||
(init_data->no_output_qs && !init_data->output_handler))
@@ -1340,16 +1236,13 @@ int qdio_allocate(struct qdio_initialize *init_data)
(!init_data->output_sbal_addr_array))
return -EINVAL;
- qdio_allocate_do_dbf(init_data);
-
/* irq_ptr must be in GFP_DMA since it contains ccw1.cda */
irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!irq_ptr)
goto out_err;
- QDIO_DBF_TEXT0(0, setup, "irq_ptr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr, sizeof(void *));
mutex_init(&irq_ptr->setup_mutex);
+ qdio_allocate_dbf(init_data, irq_ptr);
/*
* Allocate a page for the chsc calls in qdio_establish.
@@ -1367,9 +1260,6 @@ int qdio_allocate(struct qdio_initialize *init_data)
goto out_rel;
WARN_ON((unsigned long)irq_ptr->qdr & 0xfff);
- QDIO_DBF_TEXT0(0, setup, "qdr:");
- QDIO_DBF_HEX0(0, setup, &irq_ptr->qdr, sizeof(void *));
-
if (qdio_allocate_qs(irq_ptr, init_data->no_input_qs,
init_data->no_output_qs))
goto out_rel;
@@ -1390,14 +1280,12 @@ EXPORT_SYMBOL_GPL(qdio_allocate);
*/
int qdio_establish(struct qdio_initialize *init_data)
{
- char dbf_text[20];
struct qdio_irq *irq_ptr;
struct ccw_device *cdev = init_data->cdev;
unsigned long saveflags;
int rc;
- sprintf(dbf_text, "qest%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qestablish:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1427,10 +1315,8 @@ int qdio_establish(struct qdio_initialize *init_data)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ESTABLISH, 0, 0);
if (rc) {
- sprintf(dbf_text, "eq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "eq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x est IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1451,10 +1337,8 @@ int qdio_establish(struct qdio_initialize *init_data)
}
qdio_setup_ssqd_info(irq_ptr);
- sprintf(dbf_text, "qDmmwc%2x", irq_ptr->ssqd_desc.mmwc);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- sprintf(dbf_text, "qib ac%2x", irq_ptr->qib.ac);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc);
+ DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac);
/* qebsm is now setup if available, initialize buffer states */
qdio_init_buf_states(irq_ptr);
@@ -1475,10 +1359,8 @@ int qdio_activate(struct ccw_device *cdev)
struct qdio_irq *irq_ptr;
int rc;
unsigned long saveflags;
- char dbf_text[20];
- sprintf(dbf_text, "qact%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("qactivate:%4x", cdev->private->schid.sch_no);
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr)
@@ -1504,10 +1386,8 @@ int qdio_activate(struct ccw_device *cdev)
rc = ccw_device_start(cdev, &irq_ptr->ccw, QDIO_DOING_ACTIVATE,
0, DOIO_DENY_PREFETCH);
if (rc) {
- sprintf(dbf_text, "aq:io%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
- sprintf(dbf_text, "aq:rc%4x", rc);
- QDIO_DBF_TEXT2(1, setup, dbf_text);
+ DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%4x", rc);
}
spin_unlock_irqrestore(get_ccwdev_lock(cdev), saveflags);
@@ -1565,23 +1445,38 @@ static inline int buf_in_between(int bufnr, int start, int count)
static void handle_inbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count)
{
- unsigned long flags;
- int used, rc;
+ int used, cc, diff;
- /*
- * do_QDIO could run in parallel with the queue tasklet so the
- * upper-layer programm could empty the ACK'ed buffer here.
- * If that happens we must clear the polling flag, otherwise
- * qdio_stop_polling() could set the buffer to NOT_INIT after
- * it was set to EMPTY which would kill us.
- */
- spin_lock_irqsave(&q->u.in.lock, flags);
- if (q->u.in.polling)
- if (buf_in_between(q->last_move_ftc, bufnr, count))
+ if (!q->u.in.polling)
+ goto set;
+
+ /* protect against stop polling setting an ACK for an emptied slsb */
+ if (count == QDIO_MAX_BUFFERS_PER_Q) {
+ /* overwriting everything, just delete polling status */
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ goto set;
+ } else if (buf_in_between(q->last_move_ftc, bufnr, count)) {
+ if (is_qebsm(q)) {
+ /* partial overwrite, just update last_move_ftc */
+ diff = add_buf(bufnr, count);
+ diff = sub_buf(diff, q->last_move_ftc);
+ q->u.in.ack_count -= diff;
+ if (q->u.in.ack_count <= 0) {
+ q->u.in.polling = 0;
+ q->u.in.ack_count = 0;
+ /* TODO: must we set last_move_ftc to something meaningful? */
+ goto set;
+ }
+ q->last_move_ftc = add_buf(q->last_move_ftc, diff);
+ }
+ else
+ /* the only ACK will be deleted, so stop polling */
q->u.in.polling = 0;
+ }
+set:
count = set_buf_states(q, bufnr, SLSB_CU_INPUT_EMPTY, count);
- spin_unlock_irqrestore(&q->u.in.lock, flags);
used = atomic_add_return(count, &q->nr_buf_used) - count;
BUG_ON(used + count > QDIO_MAX_BUFFERS_PER_Q);
@@ -1591,9 +1486,9 @@ static void handle_inbound(struct qdio_q *q, unsigned int callflags,
return;
if (need_siga_in(q)) {
- rc = qdio_siga_input(q);
- if (rc)
- q->qdio_error = rc;
+ cc = qdio_siga_input(q);
+ if (cc)
+ q->qdio_error = cc;
}
}
@@ -1640,6 +1535,10 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
while (count--)
qdio_kick_outbound_q(q);
}
+
+ /* report CC=2 conditions synchronously */
+ if (q->qdio_error)
+ __qdio_outbound_processing(q);
goto out;
}
@@ -1649,11 +1548,11 @@ static void handle_outbound(struct qdio_q *q, unsigned int callflags,
}
/* try to fast requeue buffers */
- get_buf_state(q, prev_buf(bufnr), &state);
+ get_buf_state(q, prev_buf(bufnr), &state, 0);
if (state != SLSB_CU_OUTPUT_PRIMED)
qdio_kick_outbound_q(q);
else {
- QDIO_DBF_TEXT5(0, trace, "fast-req");
+ DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "fast-req");
qdio_perf_stat_inc(&perf_stats.fast_requeue);
}
out:
@@ -1673,12 +1572,6 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
int q_nr, int bufnr, int count)
{
struct qdio_irq *irq_ptr;
-#ifdef CONFIG_QDIO_DEBUG
- char dbf_text[20];
-
- sprintf(dbf_text, "doQD%4x", cdev->private->schid.sch_no);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) ||
(count > QDIO_MAX_BUFFERS_PER_Q) ||
@@ -1692,33 +1585,24 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags,
if (!irq_ptr)
return -ENODEV;
-#ifdef CONFIG_QDIO_DEBUG
if (callflags & QDIO_FLAG_SYNC_INPUT)
- QDIO_DBF_HEX3(0, trace, &irq_ptr->input_qs[q_nr],
- sizeof(void *));
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO input");
else
- QDIO_DBF_HEX3(0, trace, &irq_ptr->output_qs[q_nr],
- sizeof(void *));
-
- sprintf(dbf_text, "flag%04x", callflags);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
- sprintf(dbf_text, "qi%02xct%02x", bufnr, count);
- QDIO_DBF_TEXT3(0, trace, dbf_text);
-#endif /* CONFIG_QDIO_DEBUG */
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "doQDIO output");
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "q:%1d flag:%4x", q_nr, callflags);
+ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "buf:%2d cnt:%3d", bufnr, count);
if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)
return -EBUSY;
if (callflags & QDIO_FLAG_SYNC_INPUT)
- handle_inbound(irq_ptr->input_qs[q_nr],
- callflags, bufnr, count);
+ handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr,
+ count);
else if (callflags & QDIO_FLAG_SYNC_OUTPUT)
- handle_outbound(irq_ptr->output_qs[q_nr],
- callflags, bufnr, count);
- else {
- QDIO_DBF_TEXT3(1, trace, "doQD:inv");
+ handle_outbound(irq_ptr->output_qs[q_nr], callflags, bufnr,
+ count);
+ else
return -EINVAL;
- }
return 0;
}
EXPORT_SYMBOL_GPL(do_QDIO);
diff --git a/drivers/s390/cio/qdio_perf.c b/drivers/s390/cio/qdio_perf.c
index ec5c4a41423..136d0f0b1e9 100644
--- a/drivers/s390/cio/qdio_perf.c
+++ b/drivers/s390/cio/qdio_perf.c
@@ -74,12 +74,20 @@ static int qdio_perf_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\n");
seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n",
(long)atomic_long_read(&perf_stats.fast_requeue));
+ seq_printf(m, "Number of outbound target full condition\t: %li\n",
+ (long)atomic_long_read(&perf_stats.outbound_target_full));
seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_tl_out_timer));
seq_printf(m, "Number of stop polling calls\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_stop_polling));
seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n",
(long)atomic_long_read(&perf_stats.thinint_inbound_loop2));
+ seq_printf(m, "QEBSM EQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_eqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_eqbs_incomplete));
+ seq_printf(m, "QEBSM SQBS total/incomplete\t\t\t: %li/%li\n",
+ (long)atomic_long_read(&perf_stats.debug_sqbs_all),
+ (long)atomic_long_read(&perf_stats.debug_sqbs_incomplete));
seq_printf(m, "\n");
return 0;
}
diff --git a/drivers/s390/cio/qdio_perf.h b/drivers/s390/cio/qdio_perf.h
index 5c406a8b738..7821ac4fa51 100644
--- a/drivers/s390/cio/qdio_perf.h
+++ b/drivers/s390/cio/qdio_perf.h
@@ -36,10 +36,15 @@ struct qdio_perf_stats {
atomic_long_t inbound_handler;
atomic_long_t outbound_handler;
atomic_long_t fast_requeue;
+ atomic_long_t outbound_target_full;
/* for debugging */
atomic_long_t debug_tl_out_timer;
atomic_long_t debug_stop_polling;
+ atomic_long_t debug_eqbs_all;
+ atomic_long_t debug_eqbs_incomplete;
+ atomic_long_t debug_sqbs_all;
+ atomic_long_t debug_sqbs_incomplete;
};
extern struct qdio_perf_stats perf_stats;
diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c
index a0b6b46e746..c08356b95bf 100644
--- a/drivers/s390/cio/qdio_setup.c
+++ b/drivers/s390/cio/qdio_setup.c
@@ -117,17 +117,16 @@ static void setup_queues_misc(struct qdio_q *q, struct qdio_irq *irq_ptr,
q->mask = 1 << (31 - i);
q->nr = i;
q->handler = handler;
+ spin_lock_init(&q->lock);
}
static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
- void **sbals_array, char *dbf_text, int i)
+ void **sbals_array, int i)
{
struct qdio_q *prev;
int j;
- QDIO_DBF_TEXT0(0, setup, dbf_text);
- QDIO_DBF_HEX0(0, setup, &q, sizeof(void *));
-
+ DBF_HEX(&q, sizeof(void *));
q->sl = (struct sl *)((char *)q->slib + PAGE_SIZE / 2);
/* fill in sbal */
@@ -150,31 +149,26 @@ static void setup_storage_lists(struct qdio_q *q, struct qdio_irq *irq_ptr,
for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; j++)
q->sl->element[j].sbal = (unsigned long)q->sbal[j];
- QDIO_DBF_TEXT2(0, setup, "sl-sb-b0");
- QDIO_DBF_HEX2(0, setup, q->sl, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, &q->slsb, sizeof(void *));
- QDIO_DBF_HEX2(0, setup, q->sbal, sizeof(void *));
+ DBF_EVENT("sl-slsb-sbal");
+ DBF_HEX(q->sl, sizeof(void *));
+ DBF_HEX(&q->slsb, sizeof(void *));
+ DBF_HEX(q->sbal, sizeof(void *));
}
static void setup_queues(struct qdio_irq *irq_ptr,
struct qdio_initialize *qdio_init)
{
- char dbf_text[20];
struct qdio_q *q;
void **input_sbal_array = qdio_init->input_sbal_addr_array;
void **output_sbal_array = qdio_init->output_sbal_addr_array;
int i;
- sprintf(dbf_text, "qset%4x", qdio_init->cdev->private->schid.sch_no);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
-
for_each_input_queue(irq_ptr, q, i) {
- sprintf(dbf_text, "in-q%4x", i);
+ DBF_EVENT("in-q:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i);
q->is_input_q = 1;
- spin_lock_init(&q->u.in.lock);
- setup_storage_lists(q, irq_ptr, input_sbal_array, dbf_text, i);
+ setup_storage_lists(q, irq_ptr, input_sbal_array, i);
input_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
if (is_thinint_irq(irq_ptr))
@@ -186,12 +180,11 @@ static void setup_queues(struct qdio_irq *irq_ptr,
}
for_each_output_queue(irq_ptr, q, i) {
- sprintf(dbf_text, "outq%4x", i);
+ DBF_EVENT("outq:%1d", i);
setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i);
q->is_input_q = 0;
- setup_storage_lists(q, irq_ptr, output_sbal_array,
- dbf_text, i);
+ setup_storage_lists(q, irq_ptr, output_sbal_array, i);
output_sbal_array += QDIO_MAX_BUFFERS_PER_Q;
tasklet_init(&q->tasklet, qdio_outbound_processing,
@@ -222,8 +215,6 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac)
static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
unsigned char qdioac, unsigned long token)
{
- char dbf_text[15];
-
if (!(irq_ptr->qib.rflags & QIB_RFLAGS_ENABLE_QEBSM))
goto no_qebsm;
if (!(qdioac & AC1_SC_QEBSM_AVAILABLE) ||
@@ -232,33 +223,41 @@ static void check_and_setup_qebsm(struct qdio_irq *irq_ptr,
irq_ptr->sch_token = token;
- QDIO_DBF_TEXT0(0, setup, "V=V:1");
- sprintf(dbf_text, "%8lx", irq_ptr->sch_token);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("V=V:1");
+ DBF_EVENT("%8lx", irq_ptr->sch_token);
return;
no_qebsm:
irq_ptr->sch_token = 0;
irq_ptr->qib.rflags &= ~QIB_RFLAGS_ENABLE_QEBSM;
- QDIO_DBF_TEXT0(0, setup, "noV=V");
+ DBF_EVENT("noV=V");
}
-static int __get_ssqd_info(struct qdio_irq *irq_ptr)
+/*
+ * If there is a qdio_irq we use the chsc_page and store the information
+ * in the qdio_irq, otherwise we copy it to the specified structure.
+ */
+int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
+ struct subchannel_id *schid,
+ struct qdio_ssqd_desc *data)
{
struct chsc_ssqd_area *ssqd;
int rc;
- QDIO_DBF_TEXT0(0, setup, "getssqd");
- ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ DBF_EVENT("getssqd:%4x", schid->sch_no);
+ if (irq_ptr != NULL)
+ ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
+ else
+ ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
memset(ssqd, 0, PAGE_SIZE);
ssqd->request = (struct chsc_header) {
.length = 0x0010,
.code = 0x0024,
};
- ssqd->first_sch = irq_ptr->schid.sch_no;
- ssqd->last_sch = irq_ptr->schid.sch_no;
- ssqd->ssid = irq_ptr->schid.ssid;
+ ssqd->first_sch = schid->sch_no;
+ ssqd->last_sch = schid->sch_no;
+ ssqd->ssid = schid->ssid;
if (chsc(ssqd))
return -EIO;
@@ -268,27 +267,29 @@ static int __get_ssqd_info(struct qdio_irq *irq_ptr)
if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) ||
- (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no))
+ (ssqd->qdio_ssqd.sch != schid->sch_no))
return -EINVAL;
- memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
- sizeof(struct qdio_ssqd_desc));
+ if (irq_ptr != NULL)
+ memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ else {
+ memcpy(data, &ssqd->qdio_ssqd,
+ sizeof(struct qdio_ssqd_desc));
+ free_page((unsigned long)ssqd);
+ }
return 0;
}
void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
{
unsigned char qdioac;
- char dbf_text[15];
int rc;
- rc = __get_ssqd_info(irq_ptr);
+ rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL);
if (rc) {
- QDIO_DBF_TEXT2(0, setup, "ssqdasig");
- sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
- sprintf(dbf_text, "rc:%d", rc);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no);
+ DBF_ERROR("rc:%x", rc);
/* all flags set, worst case */
qdioac = AC1_SIGA_INPUT_NEEDED | AC1_SIGA_OUTPUT_NEEDED |
AC1_SIGA_SYNC_NEEDED;
@@ -297,9 +298,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr)
check_and_setup_qebsm(irq_ptr, qdioac, irq_ptr->ssqd_desc.sch_token);
process_ac_flags(irq_ptr, qdioac);
-
- sprintf(dbf_text, "qdioac%2x", qdioac);
- QDIO_DBF_TEXT2(0, setup, dbf_text);
+ DBF_EVENT("qdioac:%4x", qdioac);
}
void qdio_release_memory(struct qdio_irq *irq_ptr)
@@ -419,7 +418,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
/* get qdio commands */
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_EQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no eq");
+ DBF_ERROR("%4x NO EQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -427,7 +426,7 @@ int qdio_setup_irq(struct qdio_initialize *init_data)
ciw = ccw_device_get_ciw(init_data->cdev, CIW_TYPE_AQUEUE);
if (!ciw) {
- QDIO_DBF_TEXT2(1, setup, "no aq");
+ DBF_ERROR("%4x NO AQ", irq_ptr->schid.sch_no);
rc = -EINVAL;
goto out_err;
}
@@ -447,56 +446,38 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr,
{
char s[80];
- sprintf(s, "qdio: %s ", dev_name(&cdev->dev));
- switch (irq_ptr->qib.qfmt) {
- case QDIO_QETH_QFMT:
- sprintf(s + strlen(s), "OSA ");
- break;
- case QDIO_ZFCP_QFMT:
- sprintf(s + strlen(s), "ZFCP ");
- break;
- case QDIO_IQDIO_QFMT:
- sprintf(s + strlen(s), "HS ");
- break;
- }
- sprintf(s + strlen(s), "on SC %x using ", irq_ptr->schid.sch_no);
- sprintf(s + strlen(s), "AI:%d ", is_thinint_irq(irq_ptr));
- sprintf(s + strlen(s), "QEBSM:%d ", (irq_ptr->sch_token) ? 1 : 0);
- sprintf(s + strlen(s), "PCI:%d ",
- (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0);
- sprintf(s + strlen(s), "TDD:%d ", css_general_characteristics.aif_tdd);
- sprintf(s + strlen(s), "SIGA:");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.input) ? "R" : " ");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.output) ? "W" : " ");
- sprintf(s + strlen(s), "%s", (irq_ptr->siga_flag.sync) ? "S" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ");
- sprintf(s + strlen(s), "%s",
- (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
- sprintf(s + strlen(s), "\n");
+ snprintf(s, 80, "qdio: %s %s on SC %x using "
+ "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n",
+ dev_name(&cdev->dev),
+ (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" :
+ ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"),
+ irq_ptr->schid.sch_no,
+ is_thinint_irq(irq_ptr),
+ (irq_ptr->sch_token) ? 1 : 0,
+ (irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) ? 1 : 0,
+ css_general_characteristics.aif_tdd,
+ (irq_ptr->siga_flag.input) ? "R" : " ",
+ (irq_ptr->siga_flag.output) ? "W" : " ",
+ (irq_ptr->siga_flag.sync) ? "S" : " ",
+ (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ",
+ (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " ");
printk(KERN_INFO "%s", s);
}
int __init qdio_setup_init(void)
{
- char dbf_text[15];
-
qdio_q_cache = kmem_cache_create("qdio_q", sizeof(struct qdio_q),
256, 0, NULL);
if (!qdio_q_cache)
return -ENOMEM;
/* Check for OSA/FCP thin interrupts (bit 67). */
- sprintf(dbf_text, "thini%1x",
- (css_general_characteristics.aif_osa) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("thinint:%1d",
+ (css_general_characteristics.aif_osa) ? 1 : 0);
/* Check for QEBSM support in general (bit 58). */
- sprintf(dbf_text, "cssQBS:%1x",
- (qebsm_possible()) ? 1 : 0);
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("cssQEBSM:%1d", (qebsm_possible()) ? 1 : 0);
return 0;
}
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index ea7f6140026..8e90e147b74 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -125,13 +125,13 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr)
static inline int tiqdio_inbound_q_done(struct qdio_q *q)
{
- unsigned char state;
+ unsigned char state = 0;
if (!atomic_read(&q->nr_buf_used))
return 1;
qdio_siga_sync_q(q);
- get_buf_state(q, q->first_to_check, &state);
+ get_buf_state(q, q->first_to_check, &state, 0);
if (state == SLSB_P_INPUT_PRIMED)
/* more work coming */
@@ -258,8 +258,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
struct scssc_area *scssc_area;
- char dbf_text[15];
- void *ptr;
int rc;
scssc_area = (struct scssc_area *)irq_ptr->chsc_page;
@@ -294,19 +292,15 @@ static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
rc = chsc_error_from_response(scssc_area->response.code);
if (rc) {
- sprintf(dbf_text, "sidR%4x", scssc_area->response.code);
- QDIO_DBF_TEXT1(0, trace, dbf_text);
- QDIO_DBF_TEXT1(0, setup, dbf_text);
- ptr = &scssc_area->response;
- QDIO_DBF_HEX2(1, setup, &ptr, QDIO_DBF_SETUP_LEN);
+ DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no,
+ scssc_area->response.code);
+ DBF_ERROR_HEX(&scssc_area->response, sizeof(void *));
return rc;
}
- QDIO_DBF_TEXT2(0, setup, "setscind");
- QDIO_DBF_HEX2(0, setup, &scssc_area->summary_indicator_addr,
- sizeof(unsigned long));
- QDIO_DBF_HEX2(0, setup, &scssc_area->subchannel_indicator_addr,
- sizeof(unsigned long));
+ DBF_EVENT("setscind");
+ DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long));
+ DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long));
return 0;
}
@@ -327,14 +321,11 @@ void tiqdio_free_memory(void)
int __init tiqdio_register_thinints(void)
{
- char dbf_text[20];
-
isc_register(QDIO_AIRQ_ISC);
tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler,
NULL, QDIO_AIRQ_ISC);
if (IS_ERR(tiqdio_alsi)) {
- sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_alsi));
- QDIO_DBF_TEXT0(0, setup, dbf_text);
+ DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi));
tiqdio_alsi = NULL;
isc_unregister(QDIO_AIRQ_ISC);
return -ENOMEM;
@@ -360,7 +351,7 @@ void qdio_setup_thinint(struct qdio_irq *irq_ptr)
if (!is_thinint_irq(irq_ptr))
return;
irq_ptr->dsci = get_indicator();
- QDIO_DBF_HEX1(0, setup, &irq_ptr->dsci, sizeof(void *));
+ DBF_HEX(&irq_ptr->dsci, sizeof(void *));
}
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index e3fe6838293..1f5f5d2d87d 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus.
*
@@ -23,6 +24,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "ap"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -34,6 +38,10 @@
#include <linux/mutex.h>
#include <asm/s390_rdev.h>
#include <asm/reset.h>
+#include <asm/airq.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/isc.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
@@ -46,6 +54,7 @@ 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);
/*
* Module description.
@@ -68,7 +77,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
static struct device *ap_root_device = NULL;
-static DEFINE_SPINLOCK(ap_device_lock);
+static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
/*
@@ -80,19 +89,29 @@ static int ap_config_time = AP_CONFIG_TIME;
static DECLARE_WORK(ap_config_work, ap_scan_bus);
/*
- * Tasklet & timer for AP request polling.
+ * Tasklet & timer for AP request polling and interrupts
*/
static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
static atomic_t ap_poll_requests = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
static struct task_struct *ap_poll_kthread = NULL;
static DEFINE_MUTEX(ap_poll_thread_mutex);
+static void *ap_interrupt_indicator;
static struct hrtimer ap_poll_timer;
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
* If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
static unsigned long long poll_timeout = 250000;
/**
+ * ap_using_interrupts() - Returns non-zero if interrupt support is
+ * available.
+ */
+static inline int ap_using_interrupts(void)
+{
+ return ap_interrupt_indicator != NULL;
+}
+
+/**
* ap_intructions_available() - Test if AP instructions are available.
*
* Returns 0 if the AP instructions are installed.
@@ -113,6 +132,23 @@ static inline int ap_instructions_available(void)
}
/**
+ * ap_interrupts_available(): Test if AP interrupts are available.
+ *
+ * Returns 1 if AP interrupts are available.
+ */
+static int ap_interrupts_available(void)
+{
+ unsigned long long facility_bits[2];
+
+ if (stfle(facility_bits, 2) <= 1)
+ return 0;
+ if (!(facility_bits[0] & (1ULL << 61)) ||
+ !(facility_bits[1] & (1ULL << 62)))
+ return 0;
+ return 1;
+}
+
+/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
@@ -152,6 +188,80 @@ static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid)
return reg1;
}
+#ifdef CONFIG_64BIT
+/**
+ * ap_queue_interruption_control(): Enable interruption for a specific AP.
+ * @qid: The AP queue number
+ * @ind: The notification indicator byte
+ *
+ * Returns AP queue status.
+ */
+static inline struct ap_queue_status
+ap_queue_interruption_control(ap_qid_t qid, void *ind)
+{
+ register unsigned long reg0 asm ("0") = qid | 0x03000000UL;
+ register unsigned long reg1_in asm ("1") = 0x0000800000000000UL | AP_ISC;
+ register struct ap_queue_status reg1_out asm ("1");
+ register void *reg2 asm ("2") = ind;
+ asm volatile(
+ ".long 0xb2af0000" /* PQAP(RAPQ) */
+ : "+d" (reg0), "+d" (reg1_in), "=d" (reg1_out), "+d" (reg2)
+ :
+ : "cc" );
+ return reg1_out;
+}
+#endif
+
+/**
+ * ap_queue_enable_interruption(): Enable interruption on an AP.
+ * @qid: The AP queue number
+ * @ind: the notification indicator byte
+ *
+ * Enables interruption on AP queue via ap_queue_interruption_control(). Based
+ * on the return value it waits a while and tests the AP queue if interrupts
+ * have been switched on using ap_test_queue().
+ */
+static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
+{
+#ifdef CONFIG_64BIT
+ struct ap_queue_status status;
+ int t_depth, t_device_type, rc, i;
+
+ rc = -EBUSY;
+ status = ap_queue_interruption_control(qid, ind);
+
+ for (i = 0; i < AP_MAX_RESET; i++) {
+ switch (status.response_code) {
+ case AP_RESPONSE_NORMAL:
+ if (status.int_enabled)
+ return 0;
+ break;
+ case AP_RESPONSE_RESET_IN_PROGRESS:
+ case AP_RESPONSE_BUSY:
+ break;
+ case AP_RESPONSE_Q_NOT_AVAIL:
+ case AP_RESPONSE_DECONFIGURED:
+ case AP_RESPONSE_CHECKSTOPPED:
+ case AP_RESPONSE_INVALID_ADDRESS:
+ return -ENODEV;
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ if (status.int_enabled)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ if (i < AP_MAX_RESET - 1) {
+ udelay(5);
+ status = ap_test_queue(qid, &t_depth, &t_device_type);
+ }
+ }
+ return rc;
+#else
+ return -EINVAL;
+#endif
+}
+
/**
* __ap_send(): Send message to adjunct processor queue.
* @qid: The AP queue number
@@ -295,6 +405,11 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
case AP_RESPONSE_CHECKSTOPPED:
rc = -ENODEV;
break;
+ case AP_RESPONSE_INVALID_ADDRESS:
+ rc = -ENODEV;
+ break;
+ case AP_RESPONSE_OTHERWISE_CHANGED:
+ break;
case AP_RESPONSE_BUSY:
break;
default:
@@ -345,6 +460,15 @@ static int ap_init_queue(ap_qid_t qid)
status = ap_test_queue(qid, &dummy, &dummy);
}
}
+ if (rc == 0 && ap_using_interrupts()) {
+ rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator);
+ /* If interruption mode is supported by the machine,
+ * but an AP can not be enabled for interruption then
+ * the AP will be discarded. */
+ if (rc)
+ pr_err("Registering adapter interrupts for "
+ "AP %d failed\n", AP_QID_DEVICE(qid));
+ }
return rc;
}
@@ -397,16 +521,16 @@ static ssize_t ap_hwtype_show(struct device *dev,
struct ap_device *ap_dev = to_ap_dev(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type);
}
-static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
+static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ap_device *ap_dev = to_ap_dev(dev);
return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth);
}
-static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
+static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
static ssize_t ap_request_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -509,9 +633,9 @@ static int ap_device_probe(struct device *dev)
ap_dev->drv = ap_drv;
rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
if (!rc) {
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_add(&ap_dev->list, &ap_device_list);
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
}
return rc;
}
@@ -553,9 +677,9 @@ static int ap_device_remove(struct device *dev)
ap_flush_queue(ap_dev);
del_timer_sync(&ap_dev->timeout);
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_del_init(&ap_dev->list);
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
if (ap_drv->remove)
ap_drv->remove(ap_dev);
spin_lock_bh(&ap_dev->lock);
@@ -599,6 +723,14 @@ static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
}
+static ssize_t ap_interrupts_show(struct bus_type *bus, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ap_using_interrupts() ? 1 : 0);
+}
+
+static BUS_ATTR(ap_interrupts, 0444, ap_interrupts_show, NULL);
+
static ssize_t ap_config_time_store(struct bus_type *bus,
const char *buf, size_t count)
{
@@ -653,7 +785,8 @@ static ssize_t poll_timeout_store(struct bus_type *bus, const char *buf,
ktime_t hr_time;
/* 120 seconds = maximum poll interval */
- if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 || time > 120000000000)
+ if (sscanf(buf, "%llu\n", &time) != 1 || time < 1 ||
+ time > 120000000000ULL)
return -EINVAL;
poll_timeout = time;
hr_time = ktime_set(0, poll_timeout);
@@ -672,6 +805,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
&bus_attr_ap_domain,
&bus_attr_config_time,
&bus_attr_poll_thread,
+ &bus_attr_ap_interrupts,
&bus_attr_poll_timeout,
NULL,
};
@@ -814,6 +948,11 @@ out:
return rc;
}
+static void ap_interrupt_handler(void *unused1, void *unused2)
+{
+ tasklet_schedule(&ap_tasklet);
+}
+
/**
* __ap_scan_bus(): Scan the AP bus.
* @dev: Pointer to device
@@ -928,6 +1067,8 @@ ap_config_timeout(unsigned long ptr)
*/
static inline void ap_schedule_poll_timer(void)
{
+ if (ap_using_interrupts())
+ return;
if (hrtimer_is_queued(&ap_poll_timer))
return;
hrtimer_start(&ap_poll_timer, ktime_set(0, poll_timeout),
@@ -1181,7 +1322,7 @@ static void ap_reset(struct ap_device *ap_dev)
ap_dev->unregistered = 1;
}
-static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags)
+static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
{
spin_lock(&ap_dev->lock);
if (!ap_dev->unregistered) {
@@ -1207,13 +1348,19 @@ 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((u8 *)ap_interrupt_indicator, 0);
do {
flags = 0;
- spin_lock(&ap_device_lock);
+ spin_lock(&ap_device_list_lock);
list_for_each_entry(ap_dev, &ap_device_list, list) {
- __ap_poll_all(ap_dev, &flags);
+ __ap_poll_device(ap_dev, &flags);
}
- spin_unlock(&ap_device_lock);
+ spin_unlock(&ap_device_list_lock);
} while (flags & 1);
if (flags & 2)
ap_schedule_poll_timer();
@@ -1253,11 +1400,11 @@ static int ap_poll_thread(void *data)
remove_wait_queue(&ap_poll_wait, &wait);
flags = 0;
- spin_lock_bh(&ap_device_lock);
+ spin_lock_bh(&ap_device_list_lock);
list_for_each_entry(ap_dev, &ap_device_list, list) {
- __ap_poll_all(ap_dev, &flags);
+ __ap_poll_device(ap_dev, &flags);
}
- spin_unlock_bh(&ap_device_lock);
+ spin_unlock_bh(&ap_device_list_lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&ap_poll_wait, &wait);
@@ -1268,6 +1415,8 @@ static int ap_poll_thread_start(void)
{
int rc;
+ if (ap_using_interrupts())
+ return 0;
mutex_lock(&ap_poll_thread_mutex);
if (!ap_poll_kthread) {
ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
@@ -1301,8 +1450,12 @@ static void ap_request_timeout(unsigned long data)
{
struct ap_device *ap_dev = (struct ap_device *) data;
- if (ap_dev->reset == AP_RESET_ARMED)
+ if (ap_dev->reset == AP_RESET_ARMED) {
ap_dev->reset = AP_RESET_DO;
+
+ if (ap_using_interrupts())
+ tasklet_schedule(&ap_tasklet);
+ }
}
static void ap_reset_domain(void)
@@ -1337,14 +1490,25 @@ int __init ap_module_init(void)
int rc, i;
if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
- printk(KERN_WARNING "Invalid param: domain = %d. "
- " Not loading.\n", ap_domain_index);
+ pr_warning("%d is not a valid cryptographic domain\n",
+ ap_domain_index);
return -EINVAL;
}
if (ap_instructions_available() != 0) {
- printk(KERN_WARNING "AP instructions not installed.\n");
+ pr_warning("The hardware system does not support "
+ "AP instructions\n");
return -ENODEV;
}
+ if (ap_interrupts_available()) {
+ isc_register(AP_ISC);
+ ap_interrupt_indicator = s390_register_adapter_interrupt(
+ &ap_interrupt_handler, NULL, AP_ISC);
+ if (IS_ERR(ap_interrupt_indicator)) {
+ ap_interrupt_indicator = NULL;
+ isc_unregister(AP_ISC);
+ }
+ }
+
register_reset_call(&ap_reset_call);
/* Create /sys/bus/ap. */
@@ -1408,6 +1572,10 @@ out_bus:
bus_unregister(&ap_bus_type);
out:
unregister_reset_call(&ap_reset_call);
+ if (ap_using_interrupts()) {
+ s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
+ isc_unregister(AP_ISC);
+ }
return rc;
}
@@ -1443,6 +1611,10 @@ void ap_module_exit(void)
bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
bus_unregister(&ap_bus_type);
unregister_reset_call(&ap_reset_call);
+ if (ap_using_interrupts()) {
+ s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC);
+ isc_unregister(AP_ISC);
+ }
}
#ifndef CONFIG_ZCRYPT_MONOLITHIC
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 446378b308f..a3536224180 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -5,6 +5,7 @@
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * Felix Beck <felix.beck@de.ibm.com>
*
* Adjunct processor bus header file.
*
@@ -67,7 +68,8 @@ struct ap_queue_status {
unsigned int queue_empty : 1;
unsigned int replies_waiting : 1;
unsigned int queue_full : 1;
- unsigned int pad1 : 5;
+ unsigned int pad1 : 4;
+ unsigned int int_enabled : 1;
unsigned int response_code : 8;
unsigned int pad2 : 16;
};
@@ -78,6 +80,8 @@ struct ap_queue_status {
#define AP_RESPONSE_DECONFIGURED 0x03
#define AP_RESPONSE_CHECKSTOPPED 0x04
#define AP_RESPONSE_BUSY 0x05
+#define AP_RESPONSE_INVALID_ADDRESS 0x06
+#define AP_RESPONSE_OTHERWISE_CHANGED 0x07
#define AP_RESPONSE_Q_FULL 0x10
#define AP_RESPONSE_NO_PENDING_REPLY 0x10
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 54f4cbc3be9..326ea08f67c 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -264,17 +264,21 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type80_hdr *t80h = reply->message;
+ struct type80_hdr *t80h;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t80h->type == TYPE80_RSP_CODE) {
+ goto out;
+ }
+ t80h = reply->message;
+ if (t80h->type == TYPE80_RSP_CODE) {
length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len);
memcpy(msg->message, reply->message, length);
} else
memcpy(msg->message, reply->message, sizeof error_reply);
+out:
complete((struct completion *) msg->private);
}
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index 12da4815ba8..17ba81b58c7 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -247,17 +247,21 @@ static void zcrypt_pcica_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type84_hdr *t84h = reply->message;
+ struct type84_hdr *t84h;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t84h->code == TYPE84_RSP_CODE) {
+ 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);
}
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index 779952cb19f..f4b0c479543 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -447,19 +447,23 @@ static void zcrypt_pcicc_receive(struct ap_device *ap_dev,
.type = TYPE82_RSP_CODE,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
- struct type86_reply *t86r = reply->message;
+ struct type86_reply *t86r;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ 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);
}
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index d8ad36f8154..e7a1e22e77a 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -635,13 +635,16 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
};
struct response_type *resp_type =
(struct response_type *) msg->private;
- struct type86x_reply *t86r = reply->message;
+ struct type86x_reply *t86r;
int length;
/* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply))
+ if (IS_ERR(reply)) {
memcpy(msg->message, &error_reply, sizeof(error_reply));
- else if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ goto out;
+ }
+ t86r = reply->message;
+ if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprbx.cprb_ver_id == 0x02) {
switch (resp_type->type) {
case PCIXCC_RESPONSE_TYPE_ICA:
@@ -660,6 +663,7 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
}
} else
memcpy(msg->message, reply->message, sizeof error_reply);
+out:
complete(&(resp_type->work));
}
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 42776550acf..f29c7086fc1 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -13,6 +13,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -190,21 +193,22 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg);
void ctcm_ccw_check_rc(struct channel *ch, int rc, char *msg)
{
CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
- "%s(%s): %s: %04x\n",
- CTCM_FUNTAIL, ch->id, msg, rc);
+ "%s(%s): %s: %04x\n",
+ CTCM_FUNTAIL, ch->id, msg, rc);
switch (rc) {
case -EBUSY:
- ctcm_pr_warn("%s (%s): Busy !\n", ch->id, msg);
+ pr_info("%s: The communication peer is busy\n",
+ ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_EBUSY, ch);
break;
case -ENODEV:
- ctcm_pr_emerg("%s (%s): Invalid device called for IO\n",
- ch->id, msg);
+ pr_err("%s: The specified target device is not valid\n",
+ ch->id);
fsm_event(ch->fsm, CTC_EVENT_IO_ENODEV, ch);
break;
default:
- ctcm_pr_emerg("%s (%s): Unknown error in do_IO %04x\n",
- ch->id, msg, rc);
+ pr_err("An I/O operation resulted in error %04x\n",
+ rc);
fsm_event(ch->fsm, CTC_EVENT_IO_UNKNOWN, ch);
}
}
@@ -886,8 +890,15 @@ static void ctcm_chx_rxiniterr(fsm_instance *fi, int event, void *arg)
fsm_newstate(fi, CTC_STATE_RXERR);
fsm_event(priv->fsm, DEV_EVENT_RXDOWN, dev);
}
- } else
- ctcm_pr_warn("%s: Error during RX init handshake\n", dev->name);
+ } else {
+ CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
+ "%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
+ ctc_ch_event_names[event], fsm_getstate_str(fi));
+
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
+ }
}
/**
@@ -969,7 +980,9 @@ static void ctcm_chx_txiniterr(fsm_instance *fi, int event, void *arg)
"%s(%s): %s in %s", CTCM_FUNTAIL, ch->id,
ctc_ch_event_names[event], fsm_getstate_str(fi));
- ctcm_pr_warn("%s: Error during TX init handshake\n", dev->name);
+ dev_warn(&dev->dev,
+ "Initialization failed with RX/TX init handshake "
+ "error %s\n", ctc_ch_event_names[event]);
}
}
@@ -2101,14 +2114,11 @@ static void dev_action_restart(fsm_instance *fi, int event, void *arg)
CTCMY_DBF_DEV_NAME(TRACE, dev, "");
if (IS_MPC(priv)) {
- ctcm_pr_info("ctcm: %s Restarting Device and "
- "MPC Group in 5 seconds\n",
- dev->name);
restart_timer = CTCM_TIME_1_SEC;
} else {
- ctcm_pr_info("%s: Restarting\n", dev->name);
restart_timer = CTCM_TIME_5_SEC;
}
+ dev_info(&dev->dev, "Restarting device\n");
dev_action_stop(fi, event, arg);
fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
@@ -2150,16 +2160,16 @@ static void dev_action_chup(fsm_instance *fi, int event, void *arg)
case DEV_STATE_STARTWAIT_RX:
if (event == DEV_EVENT_RXUP) {
fsm_newstate(fi, DEV_STATE_RUNNING);
- ctcm_pr_info("%s: connected with remote side\n",
- dev->name);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
ctcm_clear_busy(dev);
}
break;
case DEV_STATE_STARTWAIT_TX:
if (event == DEV_EVENT_TXUP) {
fsm_newstate(fi, DEV_STATE_RUNNING);
- ctcm_pr_info("%s: connected with remote side\n",
- dev->name);
+ dev_info(&dev->dev,
+ "Connected with remote side\n");
ctcm_clear_busy(dev);
}
break;
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index a4e29836a2a..2678573bece 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -21,6 +21,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -281,14 +284,16 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- ctcm_pr_warn("i/o-error on device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An I/O-error occurred on the CTCM device\n");
break;
case -ETIMEDOUT:
- ctcm_pr_warn("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An adapter hardware operation timed out\n");
break;
default:
- ctcm_pr_warn("unknown error %ld on device %s\n",
- PTR_ERR(irb), dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "An error occurred on the adapter hardware\n");
}
return PTR_ERR(irb);
}
@@ -309,15 +314,17 @@ static inline void ccw_unit_check(struct channel *ch, __u8 sense)
if (sense & SNS0_INTERVENTION_REQ) {
if (sense & 0x01) {
if (ch->sense_rc != 0x01) {
- ctcm_pr_debug("%s: Interface disc. or Sel. "
- "reset (remote)\n", ch->id);
+ pr_notice(
+ "%s: The communication peer has "
+ "disconnected\n", ch->id);
ch->sense_rc = 0x01;
}
fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
} else {
if (ch->sense_rc != SNS0_INTERVENTION_REQ) {
- ctcm_pr_debug("%s: System reset (remote)\n",
- ch->id);
+ pr_notice(
+ "%s: The remote operating system is "
+ "not available\n", ch->id);
ch->sense_rc = SNS0_INTERVENTION_REQ;
}
fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
@@ -1194,8 +1201,11 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
/* Check for unsolicited interrupts. */
if (cgdev == NULL) {
- ctcm_pr_warn("ctcm: Got unsolicited irq: c-%02x d-%02x\n",
- cstat, dstat);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR,
+ "%s(%s) unsolicited irq: c-%02x d-%02x\n",
+ CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat);
+ dev_warn(&cdev->dev,
+ "The adapter received a non-specific IRQ\n");
return;
}
@@ -1207,31 +1217,34 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
else if (priv->channel[WRITE]->cdev == cdev)
ch = priv->channel[WRITE];
else {
- ctcm_pr_err("ctcm: Can't determine channel for interrupt, "
- "device %s\n", dev_name(&cdev->dev));
+ dev_err(&cdev->dev,
+ "%s: Internal error: Can't determine channel for "
+ "interrupt device %s\n",
+ __func__, dev_name(&cdev->dev));
+ /* Explain: inconsistent internal structures */
return;
}
dev = ch->netdev;
if (dev == NULL) {
- ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n",
- __func__, dev_name(&cdev->dev), ch);
+ dev_err(&cdev->dev,
+ "%s Internal error: net_device is NULL, ch = 0x%p\n",
+ __func__, ch);
+ /* Explain: inconsistent internal structures */
return;
}
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
- "%s(%s): int. for %s: cstat=%02x dstat=%02x",
- CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
-
/* Copy interruption response block. */
memcpy(ch->irb, irb, sizeof(struct irb));
+ /* Issue error message and return on subchannel error code */
if (irb->scsw.cmd.cstat) {
- /* Check for good subchannel return code, otherwise error message */
fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
- ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
- dev->name, ch->id, irb->scsw.cmd.cstat,
- irb->scsw.cmd.dstat);
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
+ "%s(%s): sub-ch check %s: cs=%02x ds=%02x",
+ CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
+ dev_warn(&cdev->dev,
+ "A check occurred on the subchannel\n");
return;
}
@@ -1239,7 +1252,7 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
if ((irb->ecw[0] & ch->sense_rc) == 0)
/* print it only once */
- CTCM_DBF_TEXT_(TRACE, CTC_DBF_INFO,
+ CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
"%s(%s): sense=%02x, ds=%02x",
CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat);
ccw_unit_check(ch, irb->ecw[0]);
@@ -1574,6 +1587,11 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
+ dev_info(&dev->dev,
+ "setup OK : r/w = %s/%s, protocol : %d\n",
+ priv->channel[READ]->id,
+ priv->channel[WRITE]->id, priv->protocol);
+
CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
"setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
priv->channel[READ]->id,
@@ -1687,7 +1705,7 @@ static void __exit ctcm_exit(void)
{
unregister_cu3088_discipline(&ctcm_group_driver);
ctcm_unregister_dbf_views();
- ctcm_pr_info("CTCM driver unloaded\n");
+ pr_info("CTCM driver unloaded\n");
}
/*
@@ -1695,7 +1713,7 @@ static void __exit ctcm_exit(void)
*/
static void print_banner(void)
{
- printk(KERN_INFO "CTCM driver initialized\n");
+ pr_info("CTCM driver initialized\n");
}
/**
@@ -1717,8 +1735,8 @@ static int __init ctcm_init(void)
ret = register_cu3088_discipline(&ctcm_group_driver);
if (ret) {
ctcm_unregister_dbf_views();
- ctcm_pr_crit("ctcm_init failed with register_cu3088_discipline "
- "(rc = %d)\n", ret);
+ pr_err("%s / register_cu3088_discipline failed, ret = %d\n",
+ __func__, ret);
return ret;
}
print_banner();
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index d77cce3fe4d..d925e732b7d 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -41,12 +41,6 @@
#define LOG_FLAG_NOMEM 8
#define ctcm_pr_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
-#define ctcm_pr_info(fmt, arg...) printk(KERN_INFO fmt, ##arg)
-#define ctcm_pr_notice(fmt, arg...) printk(KERN_NOTICE fmt, ##arg)
-#define ctcm_pr_warn(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
-#define ctcm_pr_emerg(fmt, arg...) printk(KERN_EMERG fmt, ##arg)
-#define ctcm_pr_err(fmt, arg...) printk(KERN_ERR fmt, ##arg)
-#define ctcm_pr_crit(fmt, arg...) printk(KERN_CRIT fmt, ##arg)
#define CTCM_PR_DEBUG(fmt, arg...) \
do { \
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 19f5d5ed85e..3db5f846bbf 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -19,6 +19,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -386,7 +389,7 @@ int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
if (grp->allocchan_callback_retries < 4) {
if (grp->allochanfunc)
grp->allochanfunc(grp->port_num,
- grp->group_max_buflen);
+ grp->group_max_buflen);
} else {
/* there are problems...bail out */
/* there may be a state mismatch so restart */
@@ -1232,8 +1235,9 @@ done:
dev_kfree_skb_any(pskb);
if (sendrc == NET_RX_DROP) {
- printk(KERN_WARNING "%s %s() NETWORK BACKLOG EXCEEDED"
- " - PACKET DROPPED\n", dev->name, __func__);
+ dev_warn(&dev->dev,
+ "The network backlog for %s is exceeded, "
+ "package dropped\n", __func__);
fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
}
@@ -1670,10 +1674,11 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
CTCM_FUNTAIL, ch->id);
}
}
-
done:
if (rc) {
- ctcm_pr_info("ctcmpc : %s() failed\n", __func__);
+ dev_warn(&dev->dev,
+ "The XID used in the MPC protocol is not valid, "
+ "rc = %d\n", rc);
priv->xid->xid2_flag2 = 0x40;
grp->saved_xid2->xid2_flag2 = 0x40;
}
diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c
index bb2d13721d3..8452bb052d6 100644
--- a/drivers/s390/net/ctcm_sysfs.c
+++ b/drivers/s390/net/ctcm_sysfs.c
@@ -10,6 +10,9 @@
#undef DEBUGDATA
#undef DEBUGCCW
+#define KMSG_COMPONENT "ctcm"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/sysfs.h>
#include "ctcm_main.h"
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 0825be87e5a..fb6c70cec25 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -26,6 +26,9 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#define KMSG_COMPONENT "lcs"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/if.h>
#include <linux/netdevice.h>
@@ -54,8 +57,6 @@
#error Cannot compile lcs.c without some net devices switched on.
#endif
-#define PRINTK_HEADER " lcs: "
-
/**
* initialization string for output
*/
@@ -96,7 +97,7 @@ lcs_register_debug_facility(void)
lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8);
lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8);
if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
- PRINT_ERR("Not enough memory for debug facility.\n");
+ pr_err("Not enough memory for debug facility.\n");
lcs_unregister_debug_facility();
return -ENOMEM;
}
@@ -503,7 +504,9 @@ lcs_start_channel(struct lcs_channel *channel)
if (rc) {
LCS_DBF_TEXT_(4,trace,"essh%s",
dev_name(&channel->ccwdev->dev));
- PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
+ dev_err(&channel->ccwdev->dev,
+ "Starting an LCS device resulted in an error,"
+ " rc=%d!\n", rc);
}
return rc;
}
@@ -640,7 +643,9 @@ __lcs_resume_channel(struct lcs_channel *channel)
if (rc) {
LCS_DBF_TEXT_(4, trace, "ersc%s",
dev_name(&channel->ccwdev->dev));
- PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
+ dev_err(&channel->ccwdev->dev,
+ "Sending data from the LCS device to the LAN failed"
+ " with rc=%d\n",rc);
} else
channel->state = LCS_CH_STATE_RUNNING;
return rc;
@@ -1086,7 +1091,7 @@ lcs_check_multicast_support(struct lcs_card *card)
cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
if (rc != 0) {
- PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
+ pr_err("Query IPAssist failed. Assuming unsupported!\n");
return -EOPNOTSUPP;
}
if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
@@ -1119,8 +1124,8 @@ list_modified:
rc = lcs_send_setipm(card, ipm);
spin_lock_irqsave(&card->ipm_lock, flags);
if (rc) {
- PRINT_INFO("Adding multicast address failed. "
- "Table possibly full!\n");
+ pr_info("Adding multicast address failed."
+ " Table possibly full!\n");
/* store ipm in failed list -> will be added
* to ipm_list again, so a retry will be done
* during the next call of this function */
@@ -1231,8 +1236,8 @@ lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev)
ipm = (struct lcs_ipm_list *)
kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
if (ipm == NULL) {
- PRINT_INFO("Not enough memory to add "
- "new multicast entry!\n");
+ pr_info("Not enough memory to add"
+ " new multicast entry!\n");
break;
}
memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
@@ -1306,18 +1311,21 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "An I/O-error occurred on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "A command timed out on the LCS device\n");
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT);
break;
default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- dev_name(&cdev->dev));
+ dev_warn(&cdev->dev,
+ "An error occurred on the LCS device, rc=%ld\n",
+ PTR_ERR(irb));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT(2, trace, " rc???");
}
@@ -1403,8 +1411,10 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
/* Check for channel and device errors presented */
rc = lcs_get_problem(cdev, irb);
if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
- PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
- dev_name(&cdev->dev), dstat, cstat);
+ dev_warn(&cdev->dev,
+ "The LCS device stopped because of an error,"
+ " dstat=0x%X, cstat=0x%X \n",
+ dstat, cstat);
if (rc) {
channel->state = LCS_CH_STATE_ERROR;
}
@@ -1761,8 +1771,8 @@ lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
lcs_schedule_recovery(card);
break;
case LCS_CMD_STOPLAN:
- PRINT_WARN("Stoplan for %s initiated by LGW.\n",
- card->dev->name);
+ pr_warning("Stoplan for %s initiated by LGW.\n",
+ card->dev->name);
if (card->dev)
netif_carrier_off(card->dev);
break;
@@ -1790,7 +1800,8 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
skb = dev_alloc_skb(skb_len);
if (skb == NULL) {
- PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
+ dev_err(&card->dev->dev,
+ " Allocating a socket buffer to interface %s failed\n",
card->dev->name);
card->stats.rx_dropped++;
return;
@@ -1886,7 +1897,8 @@ lcs_stop_device(struct net_device *dev)
(card->write.state != LCS_CH_STATE_RUNNING));
rc = lcs_stopcard(card);
if (rc)
- PRINT_ERR("Try it again!\n ");
+ dev_err(&card->dev->dev,
+ " Shutting down the LCS device failed\n ");
return rc;
}
@@ -1905,7 +1917,7 @@ lcs_open_device(struct net_device *dev)
/* initialize statistics */
rc = lcs_detect(card);
if (rc) {
- PRINT_ERR("LCS:Error in opening device!\n");
+ pr_err("Error in opening device!\n");
} else {
dev->flags |= IFF_UP;
@@ -2113,8 +2125,9 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
rc = lcs_detect(card);
if (rc) {
LCS_DBF_TEXT(2, setup, "dtctfail");
- PRINT_WARN("Detection of LCS card failed with return code "
- "%d (0x%x)\n", rc, rc);
+ dev_err(&card->dev->dev,
+ "Detecting a network adapter for LCS devices"
+ " failed with rc=%d (0x%x)\n", rc, rc);
lcs_stopcard(card);
goto out;
}
@@ -2144,7 +2157,7 @@ lcs_new_device(struct ccwgroup_device *ccwgdev)
#endif
default:
LCS_DBF_TEXT(3, setup, "errinit");
- PRINT_ERR("LCS: Initialization failed\n");
+ pr_err(" Initialization failed\n");
goto out;
}
if (!dev)
@@ -2176,13 +2189,13 @@ netdev_out:
goto out;
/* Print out supported assists: IPv6 */
- PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s IPv6 support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
+ "with" : "without");
/* Print out supported assist: Multicast */
- PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
- (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
- "with" : "without");
+ pr_info("LCS device %s %s Multicast support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
+ "with" : "without");
return 0;
out:
@@ -2248,15 +2261,16 @@ lcs_recovery(void *ptr)
return 0;
LCS_DBF_TEXT(4, trace, "recover2");
gdev = card->gdev;
- PRINT_WARN("Recovery of device %s started...\n", dev_name(&gdev->dev));
+ dev_warn(&gdev->dev,
+ "A recovery process has been started for the LCS device\n");
rc = __lcs_shutdown_device(gdev, 1);
rc = lcs_new_device(gdev);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- card->dev->name);
+ pr_info("Device %s successfully recovered!\n",
+ card->dev->name);
else
- PRINT_INFO("Device %s could not be recovered!\n",
- card->dev->name);
+ pr_info("Device %s could not be recovered!\n",
+ card->dev->name);
lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD);
return 0;
}
@@ -2308,17 +2322,17 @@ __init lcs_init_module(void)
{
int rc;
- PRINT_INFO("Loading %s\n",version);
+ pr_info("Loading %s\n", version);
rc = lcs_register_debug_facility();
LCS_DBF_TEXT(0, setup, "lcsinit");
if (rc) {
- PRINT_ERR("Initialization failed\n");
+ pr_err("Initialization failed\n");
return rc;
}
rc = register_cu3088_discipline(&lcs_group_driver);
if (rc) {
- PRINT_ERR("Initialization failed\n");
+ pr_err("Initialization failed\n");
return rc;
}
return 0;
@@ -2331,7 +2345,7 @@ __init lcs_init_module(void)
static void
__exit lcs_cleanup_module(void)
{
- PRINT_INFO("Terminating lcs module.\n");
+ pr_info("Terminating lcs module.\n");
LCS_DBF_TEXT(0, trace, "cleanup");
unregister_cu3088_discipline(&lcs_group_driver);
lcs_unregister_debug_facility();
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index 0fea51e34b5..930e2fc2a01 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -31,6 +31,9 @@
*
*/
+#define KMSG_COMPONENT "netiucv"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#undef DEBUG
#include <linux/module.h>
@@ -846,7 +849,8 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
fsm_deltimer(&conn->timer);
iucv_path_sever(conn->path, NULL);
- PRINT_INFO("%s: Remote dropped connection\n", netdev->name);
+ dev_info(privptr->dev, "The peer interface of the IUCV device"
+ " has closed the connection\n");
IUCV_DBF_TEXT(data, 2,
"conn_action_connsever: Remote dropped connection\n");
fsm_newstate(fi, CONN_STATE_STARTWAIT);
@@ -856,13 +860,15 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg)
static void conn_action_start(fsm_instance *fi, int event, void *arg)
{
struct iucv_connection *conn = arg;
+ struct net_device *netdev = conn->netdev;
+ struct netiucv_priv *privptr = netdev_priv(netdev);
int rc;
IUCV_DBF_TEXT(trace, 3, __func__);
fsm_newstate(fi, CONN_STATE_STARTWAIT);
IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n",
- conn->netdev->name, conn->userid);
+ netdev->name, conn->userid);
/*
* We must set the state before calling iucv_connect because the
@@ -876,41 +882,45 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg)
NULL, iucvMagic, conn);
switch (rc) {
case 0:
- conn->netdev->tx_queue_len = conn->path->msglim;
+ netdev->tx_queue_len = conn->path->msglim;
fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
CONN_EVENT_TIMER, conn);
return;
case 11:
- PRINT_INFO("%s: User %s is currently not available.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to z/VM guest %s\n",
+ netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 12:
- PRINT_INFO("%s: User %s is currently not ready.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_warn(privptr->dev,
+ "The IUCV device failed to connect to the peer on z/VM"
+ " guest %s\n", netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_STARTWAIT);
break;
case 13:
- PRINT_WARN("%s: Too many IUCV connections.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device would exceed the maximum"
+ " number of IUCV connections\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 14:
- PRINT_WARN("%s: User %s has too many IUCV connections.\n",
- conn->netdev->name,
- netiucv_printname(conn->userid));
+ dev_err(privptr->dev,
+ "z/VM guest %s has too many IUCV connections"
+ " to connect with the IUCV device\n",
+ netiucv_printname(conn->userid));
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
case 15:
- PRINT_WARN("%s: No IUCV authorization in CP directory.\n",
- conn->netdev->name);
+ dev_err(privptr->dev,
+ "The IUCV device cannot connect to a z/VM guest with no"
+ " IUCV authorization\n");
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
default:
- PRINT_WARN("%s: iucv_connect returned error %d\n",
- conn->netdev->name, rc);
+ dev_err(privptr->dev,
+ "Connecting the IUCV device failed with error %d\n",
+ rc);
fsm_newstate(fi, CONN_STATE_CONNERR);
break;
}
@@ -1059,8 +1069,9 @@ dev_action_connup(fsm_instance *fi, int event, void *arg)
switch (fsm_getstate(fi)) {
case DEV_STATE_STARTWAIT:
fsm_newstate(fi, DEV_STATE_RUNNING);
- PRINT_INFO("%s: connected with remote side %s\n",
- dev->name, privptr->conn->userid);
+ dev_info(privptr->dev,
+ "The IUCV device has been connected"
+ " successfully to %s\n", privptr->conn->userid);
IUCV_DBF_TEXT(setup, 3,
"connection is up and running\n");
break;
@@ -1982,6 +1993,8 @@ static ssize_t conn_write(struct device_driver *drv,
if (rc)
goto out_unreg;
+ dev_info(priv->dev, "The IUCV interface to %s has been"
+ " established successfully\n", netiucv_printname(username));
return count;
@@ -2027,10 +2040,9 @@ static ssize_t remove_write (struct device_driver *drv,
continue;
read_unlock_bh(&iucv_connection_rwlock);
if (ndev->flags & (IFF_UP | IFF_RUNNING)) {
- PRINT_WARN("netiucv: net device %s active with peer "
- "%s\n", ndev->name, priv->conn->userid);
- PRINT_WARN("netiucv: %s cannot be removed\n",
- ndev->name);
+ dev_warn(dev, "The IUCV device is connected"
+ " to %s and cannot be removed\n",
+ priv->conn->userid);
IUCV_DBF_TEXT(data, 2, "remove_write: still active\n");
return -EPERM;
}
@@ -2062,7 +2074,7 @@ static struct attribute_group *netiucv_drv_attr_groups[] = {
static void netiucv_banner(void)
{
- PRINT_INFO("NETIUCV driver initialized\n");
+ pr_info("driver initialized\n");
}
static void __exit netiucv_exit(void)
@@ -2088,7 +2100,7 @@ static void __exit netiucv_exit(void)
iucv_unregister(&netiucv_handler, 1);
iucv_unregister_dbf_views();
- PRINT_INFO("NETIUCV driver unloaded\n");
+ pr_info("driver unloaded\n");
return;
}
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index af6d6045851..d5ccce1643e 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -31,11 +31,10 @@
#include <asm/qdio.h>
#include <asm/ccwdev.h>
#include <asm/ccwgroup.h>
+#include <asm/sysinfo.h>
#include "qeth_core_mpc.h"
-#define KMSG_COMPONENT "qeth"
-
/**
* Debug Facility stuff
*/
@@ -74,11 +73,6 @@ struct qeth_dbf_info {
#define QETH_DBF_TEXT_(name, level, text...) \
qeth_dbf_longtext(QETH_DBF_##name, level, text)
-/**
- * some more debug stuff
- */
-#define PRINTK_HEADER "qeth: "
-
#define SENSE_COMMAND_REJECT_BYTE 0
#define SENSE_COMMAND_REJECT_FLAG 0x80
#define SENSE_RESETTING_EVENT_BYTE 1
@@ -733,6 +727,7 @@ struct qeth_card {
struct qeth_osn_info osn_info;
struct qeth_discipline discipline;
atomic_t force_alloc_skb;
+ struct service_level qeth_service_level;
};
struct qeth_card_list_struct {
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 52d26592c72..e783644a210 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -319,7 +322,10 @@ static int qeth_issue_next_read(struct qeth_card *card)
return -EIO;
iob = qeth_get_buffer(&card->read);
if (!iob) {
- PRINT_WARN("issue_next_read failed: no iob available!\n");
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s issue_next_read failed: no iob "
+ "available\n", dev_name(&card->gdev->dev));
return -ENOMEM;
}
qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE);
@@ -327,7 +333,8 @@ static int qeth_issue_next_read(struct qeth_card *card)
rc = ccw_device_start(card->read.ccwdev, &card->read.ccw,
(addr_t) iob, 0, 0);
if (rc) {
- PRINT_ERR("Error in starting next read ccw! rc=%i\n", rc);
+ QETH_DBF_MESSAGE(2, "%s error in starting next read ccw! "
+ "rc=%i\n", dev_name(&card->gdev->dev), rc);
atomic_set(&card->read.irq_pending, 0);
qeth_schedule_recovery(card);
wake_up(&card->wait_q);
@@ -393,10 +400,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
} else {
switch (cmd->hdr.command) {
case IPA_CMD_STOPLAN:
- PRINT_WARN("Link failure on %s (CHPID 0x%X) - "
- "there is a network problem or "
- "someone pulled the cable or "
- "disabled the port.\n",
+ dev_warn(&card->gdev->dev,
+ "The link for interface %s on CHPID"
+ " 0x%X failed\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
card->lan_online = 0;
@@ -404,9 +410,9 @@ static struct qeth_ipa_cmd *qeth_check_ipa_data(struct qeth_card *card,
netif_carrier_off(card->dev);
return NULL;
case IPA_CMD_STARTLAN:
- PRINT_INFO("Link reestablished on %s "
- "(CHPID 0x%X). Scheduling "
- "IP address reset.\n",
+ dev_info(&card->gdev->dev,
+ "The link for %s on CHPID 0x%X has"
+ " been restored\n",
QETH_CARD_IFNAME(card),
card->info.chpid);
netif_carrier_on(card->dev);
@@ -458,7 +464,7 @@ static int qeth_check_idx_response(unsigned char *buffer)
QETH_DBF_HEX(CTRL, 2, buffer, QETH_DBF_CTRL_LEN);
if ((buffer[2] & 0xc0) == 0xc0) {
- PRINT_WARN("received an IDX TERMINATE "
+ QETH_DBF_MESSAGE(2, "received an IDX TERMINATE "
"with cause code 0x%02x%s\n",
buffer[4],
((buffer[4] == 0x22) ?
@@ -744,8 +750,10 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
QETH_DBF_TEXT(TRACE, 2, "CGENCHK");
- PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
- dev_name(&cdev->dev), dstat, cstat);
+ dev_warn(&cdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s check on device dstat=x%x, cstat=x%x ",
+ dev_name(&cdev->dev), dstat, cstat);
print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET,
16, 1, irb, 64, 1);
return 1;
@@ -784,12 +792,14 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
+ QETH_DBF_MESSAGE(2, "%s i/o-error on device\n",
+ dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
+ dev_warn(&cdev->dev, "A hardware operation timed out"
+ " on the device\n");
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -ETIMEDOUT);
if (intparm == QETH_RCD_PARM) {
@@ -802,8 +812,8 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
}
break;
default:
- PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- dev_name(&cdev->dev));
+ QETH_DBF_MESSAGE(2, "%s unknown error %ld on device\n",
+ dev_name(&cdev->dev), PTR_ERR(irb));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT(TRACE, 2, " rc???");
}
@@ -869,10 +879,12 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
(dstat & DEV_STAT_UNIT_CHECK) ||
(cstat)) {
if (irb->esw.esw0.erw.cons) {
- /* TODO: we should make this s390dbf */
- PRINT_WARN("sense data available on channel %s.\n",
- CHANNEL_ID(channel));
- PRINT_WARN(" cstat 0x%X\n dstat 0x%X\n", cstat, dstat);
+ dev_warn(&channel->ccwdev->dev,
+ "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s sense data available. cstat "
+ "0x%X dstat 0x%X\n",
+ dev_name(&channel->ccwdev->dev), cstat, dstat);
print_hex_dump(KERN_WARNING, "qeth: irb ",
DUMP_PREFIX_OFFSET, 16, 1, irb, 32, 1);
print_hex_dump(KERN_WARNING, "qeth: sense data ",
@@ -1138,6 +1150,14 @@ static int qeth_setup_card(struct qeth_card *card)
return 0;
}
+static void qeth_core_sl_print(struct seq_file *m, struct service_level *slr)
+{
+ struct qeth_card *card = container_of(slr, struct qeth_card,
+ qeth_service_level);
+ seq_printf(m, "qeth: %s firmware level %s\n", CARD_BUS_ID(card),
+ card->info.mcl_level);
+}
+
static struct qeth_card *qeth_alloc_card(void)
{
struct qeth_card *card;
@@ -1157,6 +1177,8 @@ static struct qeth_card *qeth_alloc_card(void)
return NULL;
}
card->options.layer2 = -1;
+ card->qeth_service_level.seq_print = qeth_core_sl_print;
+ register_service_level(&card->qeth_service_level);
return card;
}
@@ -1175,8 +1197,8 @@ static int qeth_determine_card_type(struct qeth_card *card)
card->qdio.no_out_queues = known_devices[i][8];
card->info.is_multicast_different = known_devices[i][9];
if (qeth_is_1920_device(card)) {
- PRINT_INFO("Priority Queueing not able "
- "due to hardware limitations!\n");
+ dev_info(&card->gdev->dev,
+ "Priority Queueing not supported\n");
card->qdio.no_out_queues = 1;
card->qdio.default_out_queue = 0;
}
@@ -1185,7 +1207,8 @@ static int qeth_determine_card_type(struct qeth_card *card)
i++;
}
card->info.type = QETH_CARD_TYPE_UNKNOWN;
- PRINT_ERR("unknown card type on device %s\n", CARD_BUS_ID(card));
+ dev_err(&card->gdev->dev, "The adapter hardware is of an "
+ "unknown type\n");
return -ENOENT;
}
@@ -1368,8 +1391,8 @@ static int qeth_get_unitaddr(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "getunit");
rc = qeth_read_conf_data(card, (void **) &prcd, &length);
if (rc) {
- PRINT_ERR("qeth_read_conf_data for device %s returned %i\n",
- CARD_DDEV_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "%s qeth_read_conf_data returned %i\n",
+ dev_name(&card->gdev->dev), rc);
return rc;
}
card->info.chpid = prcd[30];
@@ -1519,7 +1542,10 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
if (rc == -ERESTARTSYS)
return rc;
if (channel->state != CH_STATE_ACTIVATING) {
- PRINT_WARN("IDX activate timed out!\n");
+ dev_warn(&channel->ccwdev->dev, "The qeth device driver"
+ " failed to recover an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s IDX activate timed out\n",
+ dev_name(&channel->ccwdev->dev));
QETH_DBF_TEXT_(SETUP, 2, "2err%d", -ETIME);
qeth_clear_cmd_buffers(channel);
return -ETIME;
@@ -1552,20 +1578,21 @@ static void qeth_idx_write_cb(struct qeth_channel *channel,
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_WDEV_ID(card));
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
else
- PRINT_ERR("IDX_ACTIVATE on write channel device %s: "
- "negative reply\n", CARD_WDEV_ID(card));
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel:"
+ " negative reply\n",
+ dev_name(&card->write.ccwdev->dev));
goto out;
}
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if ((temp & ~0x0100) != qeth_peer_func_level(card->info.func_level)) {
- PRINT_WARN("IDX_ACTIVATE on write channel device %s: "
- "function level mismatch "
- "(sent: 0x%x, received: 0x%x)\n",
- CARD_WDEV_ID(card), card->info.func_level, temp);
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on write channel: "
+ "function level mismatch (sent: 0x%x, received: "
+ "0x%x)\n", dev_name(&card->write.ccwdev->dev),
+ card->info.func_level, temp);
goto out;
}
channel->state = CH_STATE_UP;
@@ -1591,12 +1618,13 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
if (!(QETH_IS_IDX_ACT_POS_REPLY(iob->data))) {
if (QETH_IDX_ACT_CAUSE_CODE(iob->data) == 0x19)
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: "
- "adapter exclusively used by another host\n",
- CARD_RDEV_ID(card));
+ dev_err(&card->write.ccwdev->dev,
+ "The adapter is used exclusively by another "
+ "host\n");
else
- PRINT_ERR("IDX_ACTIVATE on read channel device %s: "
- "negative reply\n", CARD_RDEV_ID(card));
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel:"
+ " negative reply\n",
+ dev_name(&card->read.ccwdev->dev));
goto out;
}
@@ -1610,9 +1638,10 @@ static void qeth_idx_read_cb(struct qeth_channel *channel,
memcpy(&temp, QETH_IDX_ACT_FUNC_LEVEL(iob->data), 2);
if (temp != qeth_peer_func_level(card->info.func_level)) {
- PRINT_WARN("IDX_ACTIVATE on read channel device %s: function "
- "level mismatch (sent: 0x%x, received: 0x%x)\n",
- CARD_RDEV_ID(card), card->info.func_level, temp);
+ QETH_DBF_MESSAGE(2, "%s IDX_ACTIVATE on read channel: function "
+ "level mismatch (sent: 0x%x, received: 0x%x)\n",
+ dev_name(&card->read.ccwdev->dev),
+ card->info.func_level, temp);
goto out;
}
memcpy(&card->token.issuer_rm_r,
@@ -1686,8 +1715,9 @@ int qeth_send_control_data(struct qeth_card *card, int len,
(addr_t) iob, 0, 0);
spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
if (rc) {
- PRINT_WARN("qeth_send_control_data: "
- "ccw_device_start rc = %i\n", rc);
+ QETH_DBF_MESSAGE(2, "%s qeth_send_control_data: "
+ "ccw_device_start rc = %i\n",
+ dev_name(&card->write.ccwdev->dev), rc);
QETH_DBF_TEXT_(TRACE, 2, " err%d", rc);
spin_lock_irqsave(&card->lock, flags);
list_del_init(&reply->list);
@@ -2170,11 +2200,8 @@ static void qeth_print_status_with_portname(struct qeth_card *card)
dbf_text[i] =
(char) _ebcasc[(__u8) dbf_text[i]];
dbf_text[8] = 0;
- PRINT_INFO("Device %s/%s/%s is a%s card%s%s%s\n"
+ dev_info(&card->gdev->dev, "Device is a%s card%s%s%s\n"
"with link type %s (portname: %s)\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
qeth_get_cardname(card),
(card->info.mcl_level[0]) ? " (level: " : "",
(card->info.mcl_level[0]) ? card->info.mcl_level : "",
@@ -2187,23 +2214,17 @@ static void qeth_print_status_with_portname(struct qeth_card *card)
static void qeth_print_status_no_portname(struct qeth_card *card)
{
if (card->info.portname[0])
- PRINT_INFO("Device %s/%s/%s is a%s "
+ dev_info(&card->gdev->dev, "Device is a%s "
"card%s%s%s\nwith link type %s "
"(no portname needed by interface).\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
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
- PRINT_INFO("Device %s/%s/%s is a%s "
+ dev_info(&card->gdev->dev, "Device is a%s "
"card%s%s%s\nwith link type %s.\n",
- CARD_RDEV_ID(card),
- CARD_WDEV_ID(card),
- CARD_DDEV_ID(card),
qeth_get_cardname(card),
(card->info.mcl_level[0]) ? " (level: " : "",
(card->info.mcl_level[0]) ? card->info.mcl_level : "",
@@ -2325,7 +2346,6 @@ static int qeth_init_input_buffer(struct qeth_card *card,
* the QETH_IN_BUF_REQUEUE_THRESHOLD we should never run out off
* buffers
*/
- BUG_ON(!pool_entry);
buf->pool_entry = pool_entry;
for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {
@@ -2630,9 +2650,8 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
qeth_get_micros() -
card->perf_stats.inbound_do_qdio_start_time;
if (rc) {
- PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
- "return %i (device %s).\n",
- rc, CARD_DDEV_ID(card));
+ dev_warn(&card->gdev->dev,
+ "QDIO reported an error, rc=%i\n", rc);
QETH_DBF_TEXT(TRACE, 2, "qinberr");
QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card));
}
@@ -3730,6 +3749,7 @@ static void qeth_core_free_card(struct qeth_card *card)
free_netdev(card->dev);
kfree(card->ip_tbd_list);
qeth_free_qdio_buffers(card);
+ unregister_service_level(&card->qeth_service_level);
kfree(card);
}
@@ -3757,7 +3777,7 @@ static int qeth_core_driver_group(const char *buf, struct device *root_dev,
int qeth_core_hardsetup_card(struct qeth_card *card)
{
- struct qdio_ssqd_desc *qdio_ssqd;
+ struct qdio_ssqd_desc *ssqd;
int retries = 3;
int mpno = 0;
int rc;
@@ -3766,7 +3786,8 @@ int qeth_core_hardsetup_card(struct qeth_card *card)
atomic_set(&card->force_alloc_skb, 0);
retry:
if (retries < 3) {
- PRINT_WARN("Retrying to do IDX activates.\n");
+ QETH_DBF_MESSAGE(2, "%s Retrying to do IDX activates.\n",
+ dev_name(&card->gdev->dev));
ccw_device_set_offline(CARD_DDEV(card));
ccw_device_set_offline(CARD_WDEV(card));
ccw_device_set_offline(CARD_RDEV(card));
@@ -3792,9 +3813,16 @@ retry:
return rc;
}
- qdio_ssqd = qdio_get_ssqd_desc(CARD_DDEV(card));
- if (qdio_ssqd)
- mpno = qdio_ssqd->pcnt;
+ ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL);
+ if (!ssqd) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd);
+ if (rc == 0)
+ mpno = ssqd->pcnt;
+ kfree(ssqd);
+
if (mpno)
mpno = min(mpno - 1, QETH_MAX_PORTNO);
if (card->info.portno > mpno) {
@@ -3834,7 +3862,10 @@ retry:
}
return 0;
out:
- PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
+ dev_warn(&card->gdev->dev, "The qeth device driver failed to recover "
+ "an error on the device\n");
+ QETH_DBF_MESSAGE(2, "%s Initialization in hardsetup failed! rc=%d\n",
+ dev_name(&card->gdev->dev), rc);
return rc;
}
EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
@@ -4054,8 +4085,8 @@ int qeth_core_load_discipline(struct qeth_card *card,
break;
}
if (!card->discipline.ccwgdriver) {
- PRINT_ERR("Support for discipline %d not present\n",
- discipline);
+ dev_err(&card->gdev->dev, "There is no kernel module to "
+ "support discipline %d\n", discipline);
rc = -EINVAL;
}
return rc;
@@ -4448,7 +4479,7 @@ static int __init qeth_core_init(void)
{
int rc;
- PRINT_INFO("loading core functions\n");
+ pr_info("loading core functions\n");
INIT_LIST_HEAD(&qeth_core_card_list.list);
rwlock_init(&qeth_core_card_list.rwlock);
@@ -4488,9 +4519,10 @@ driver_err:
ccwgroup_err:
ccw_driver_unregister(&qeth_ccw_driver);
ccw_err:
+ QETH_DBF_MESSAGE(2, "Initialization failed with code %d\n", rc);
qeth_unregister_dbf_views();
out_err:
- PRINT_ERR("Initialization failed with code %d\n", rc);
+ pr_err("Initializing the qeth device driver failed\n");
return rc;
}
@@ -4503,7 +4535,7 @@ static void __exit qeth_core_exit(void)
ccw_driver_unregister(&qeth_ccw_driver);
kmem_cache_destroy(qeth_core_header_cache);
qeth_unregister_dbf_views();
- PRINT_INFO("core functions removed\n");
+ pr_info("core functions removed\n");
}
module_init(qeth_core_init);
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 1b1e80336d2..af15bc648ba 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -503,12 +506,13 @@ static int qeth_l2_send_setmac_cb(struct qeth_card *card,
card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac,
OSA_ADDR_LEN);
- PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
- "successfully registered on device %s\n",
- card->dev->dev_addr[0], card->dev->dev_addr[1],
- card->dev->dev_addr[2], card->dev->dev_addr[3],
- card->dev->dev_addr[4], card->dev->dev_addr[5],
- card->dev->name);
+ dev_info(&card->gdev->dev,
+ "MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ "successfully registered on device %s\n",
+ card->dev->dev_addr[0], card->dev->dev_addr[1],
+ card->dev->dev_addr[2], card->dev->dev_addr[3],
+ card->dev->dev_addr[4], card->dev->dev_addr[5],
+ card->dev->name);
}
return 0;
}
@@ -1015,9 +1019,8 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode)
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
if (rc == 0xe080) {
- PRINT_WARN("LAN on card %s if offline! "
- "Waiting for STARTLAN from card.\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
card->lan_online = 0;
}
return rc;
@@ -1117,8 +1120,8 @@ static int qeth_l2_recover(void *ptr)
if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
return 0;
QETH_DBF_TEXT(TRACE, 2, "recover2");
- PRINT_WARN("Recovery of device %s started ...\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
card->use_hard_stop = 1;
__qeth_l2_set_offline(card->gdev, 1);
rc = __qeth_l2_set_online(card->gdev, 1);
@@ -1126,27 +1129,27 @@ static int qeth_l2_recover(void *ptr)
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
else {
rtnl_lock();
dev_close(card->dev);
rtnl_unlock();
- PRINT_INFO("Device %s could not be recovered!\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
}
return 0;
}
static int __init qeth_l2_init(void)
{
- PRINT_INFO("register layer 2 discipline\n");
+ pr_info("register layer 2 discipline\n");
return 0;
}
static void __exit qeth_l2_exit(void)
{
- PRINT_INFO("unregister layer 2 discipline\n");
+ pr_info("unregister layer 2 discipline\n");
}
static void qeth_l2_shutdown(struct ccwgroup_device *gdev)
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index ed59fedd592..c0b30b25a5f 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -8,6 +8,9 @@
* Frank Blaschka <frank.blaschka@de.ibm.com>
*/
+#define KMSG_COMPONENT "qeth"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/string.h>
@@ -917,8 +920,8 @@ static int qeth_l3_register_addr_entry(struct qeth_card *card,
if (rc) {
QETH_DBF_TEXT(TRACE, 2, "FAILED");
qeth_l3_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
- PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n",
- buf, rc, rc);
+ dev_warn(&card->gdev->dev,
+ "Registering IP address %s failed\n", buf);
}
return rc;
}
@@ -1029,24 +1032,22 @@ static int qeth_l3_setadapter_parms(struct qeth_card *card)
QETH_DBF_TEXT(SETUP, 2, "setadprm");
if (!qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
- PRINT_WARN("set adapter parameters not supported "
- "on device %s.\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "set adapter parameters not supported.\n");
QETH_DBF_TEXT(SETUP, 2, " notsupp");
return 0;
}
rc = qeth_query_setadapterparms(card);
if (rc) {
- PRINT_WARN("couldn't set adapter parameters on device %s: "
- "x%x\n", CARD_BUS_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "%s couldn't set adapter parameters: "
+ "0x%x\n", card->gdev->dev.bus_id, rc);
return rc;
}
if (qeth_adp_supported(card, IPA_SETADP_ALTER_MAC_ADDRESS)) {
rc = qeth_setadpparms_change_macaddr(card);
if (rc)
- PRINT_WARN("couldn't get MAC address on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), rc);
+ dev_warn(&card->gdev->dev, "Reading the adapter MAC"
+ " address failed\n", rc);
}
if ((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
@@ -1160,16 +1161,17 @@ static int qeth_l3_start_ipa_arp_processing(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "ipaarp");
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "ARP processing not supported on %s!\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start ARP processing "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting ARP processing support for %s failed\n",
+ QETH_CARD_IFNAME(card));
}
return rc;
}
@@ -1181,19 +1183,21 @@ static int qeth_l3_start_ipa_ip_fragmentation(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "ipaipfrg");
if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
- PRINT_INFO("Hardware IP fragmentation not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_IP_FRAGMENTATION,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start Hardware IP fragmentation "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting IP fragmentation support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else
- PRINT_INFO("Hardware IP fragmentation enabled \n");
+ dev_info(&card->gdev->dev,
+ "Hardware IP fragmentation enabled \n");
return rc;
}
@@ -1207,17 +1211,18 @@ static int qeth_l3_start_ipa_source_mac(struct qeth_card *card)
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
- PRINT_INFO("Inbound source address not "
- "supported on %s\n", QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Inbound source address not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_SOURCE_MAC,
IPA_CMD_ASS_START, 0);
if (rc)
- PRINT_WARN("Could not start inbound source "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting proxy ARP support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
@@ -1228,19 +1233,19 @@ static int qeth_l3_start_ipa_vlan(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtvlan");
if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
- PRINT_WARN("VLAN not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_VLAN_PRIO,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start vlan "
- "assist on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting VLAN support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else {
- PRINT_INFO("VLAN enabled \n");
+ dev_info(&card->gdev->dev, "VLAN enabled\n");
}
return rc;
}
@@ -1252,19 +1257,20 @@ static int qeth_l3_start_ipa_multicast(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "stmcast");
if (!qeth_is_supported(card, IPA_MULTICASTING)) {
- PRINT_WARN("Multicast not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Multicast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_MULTICASTING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not start multicast "
- "assist on %s: rc=%i\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Starting multicast support for %s failed\n",
+ QETH_CARD_IFNAME(card));
} else {
- PRINT_INFO("Multicast enabled\n");
+ dev_info(&card->gdev->dev, "Multicast enabled\n");
card->dev->flags |= IFF_MULTICAST;
}
return rc;
@@ -1315,36 +1321,37 @@ static int qeth_l3_softsetup_ipv6(struct qeth_card *card)
rc = qeth_l3_query_ipassists(card, QETH_PROT_IPV6);
if (rc) {
- PRINT_ERR("IPv6 query ipassist failed on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_IPV6,
IPA_CMD_ASS_START, 3);
if (rc) {
- PRINT_WARN("IPv6 start assist (version 4) failed "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_IPV6,
IPA_CMD_ASS_START);
if (rc) {
- PRINT_WARN("IPV6 start assist (version 6) failed "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_err(&card->gdev->dev,
+ "Activating IPv6 support for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
rc = qeth_l3_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
IPA_CMD_ASS_START);
if (rc) {
- PRINT_WARN("Could not enable passthrough "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Enabling the passthrough mode for %s failed\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
out:
- PRINT_INFO("IPV6 enabled \n");
+ dev_info(&card->gdev->dev, "IPV6 enabled\n");
return 0;
}
#endif
@@ -1356,8 +1363,8 @@ static int qeth_l3_start_ipa_ipv6(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtipv6");
if (!qeth_is_supported(card, IPA_IPV6)) {
- PRINT_WARN("IPv6 not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "IPv6 not supported on %s\n", QETH_CARD_IFNAME(card));
return 0;
}
#ifdef CONFIG_QETH_IPV6
@@ -1373,34 +1380,35 @@ static int qeth_l3_start_ipa_broadcast(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "stbrdcst");
card->info.broadcast_capable = 0;
if (!qeth_is_supported(card, IPA_FILTERING)) {
- PRINT_WARN("Broadcast not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Broadcast not supported on %s\n",
+ QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
goto out;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Could not enable broadcasting filtering "
- "on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Enabling broadcast filtering for "
+ "%s failed\n", QETH_CARD_IFNAME(card));
goto out;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_CONFIGURE, 1);
if (rc) {
- PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev,
+ "Setting up broadcast filtering for %s failed\n",
+ QETH_CARD_IFNAME(card));
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
- PRINT_INFO("Broadcast enabled \n");
+ dev_info(&card->gdev->dev, "Broadcast enabled\n");
rc = qeth_l3_send_simple_setassparms(card, IPA_FILTERING,
IPA_CMD_ASS_ENABLE, 1);
if (rc) {
- PRINT_WARN("Could not set up broadcast echo filtering on "
- "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Setting up broadcast echo "
+ "filtering for %s failed\n", QETH_CARD_IFNAME(card));
goto out;
}
card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
@@ -1419,18 +1427,18 @@ static int qeth_l3_send_checksum_command(struct qeth_card *card)
rc = qeth_l3_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
IPA_CMD_ASS_START, 0);
if (rc) {
- PRINT_WARN("Starting Inbound HW Checksumming failed on %s: "
- "0x%x,\ncontinuing using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card), 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) {
- PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: "
- "0x%x,\ncontinuing using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s "
+ "failed, using SW checksumming\n",
+ QETH_CARD_IFNAME(card));
return rc;
}
return 0;
@@ -1443,26 +1451,30 @@ static int qeth_l3_start_ipa_checksum(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "strtcsum");
if (card->options.checksum_type == NO_CHECKSUMMING) {
- PRINT_WARN("Using no checksumming on %s.\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Using no checksumming on %s.\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
if (card->options.checksum_type == SW_CHECKSUMMING) {
- PRINT_WARN("Using SW checksumming on %s.\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Using SW checksumming on %s.\n",
+ QETH_CARD_IFNAME(card));
return 0;
}
if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
- PRINT_WARN("Inbound HW Checksumming not "
- "supported on %s,\ncontinuing "
- "using Inbound SW Checksumming\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Inbound HW Checksumming not "
+ "supported on %s,\ncontinuing "
+ "using Inbound SW Checksumming\n",
+ QETH_CARD_IFNAME(card));
card->options.checksum_type = SW_CHECKSUMMING;
return 0;
}
rc = qeth_l3_send_checksum_command(card);
if (!rc)
- PRINT_INFO("HW Checksumming (inbound) enabled \n");
+ dev_info(&card->gdev->dev,
+ "HW Checksumming (inbound) enabled\n");
return rc;
}
@@ -1474,18 +1486,20 @@ static int qeth_l3_start_ipa_tso(struct qeth_card *card)
QETH_DBF_TEXT(TRACE, 3, "sttso");
if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
- PRINT_WARN("Outbound TSO not supported on %s\n",
- QETH_CARD_IFNAME(card));
+ dev_info(&card->gdev->dev,
+ "Outbound TSO not supported on %s\n",
+ QETH_CARD_IFNAME(card));
rc = -EOPNOTSUPP;
} else {
rc = qeth_l3_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
IPA_CMD_ASS_START, 0);
if (rc)
- PRINT_WARN("Could not start outbound TSO "
- "assist on %s: rc=%i\n",
- QETH_CARD_IFNAME(card), rc);
+ dev_warn(&card->gdev->dev, "Starting outbound TCP "
+ "segmentation offload for %s failed\n",
+ QETH_CARD_IFNAME(card));
else
- PRINT_INFO("Outbound TSO enabled\n");
+ dev_info(&card->gdev->dev,
+ "Outbound TSO enabled\n");
}
if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)) {
card->options.large_send = QETH_LARGE_SEND_NO;
@@ -1578,12 +1592,8 @@ static int qeth_l3_get_unique_id_cb(struct qeth_card *card,
else {
card->info.unique_id = UNIQUE_ID_IF_CREATE_ADDR_FAILED |
UNIQUE_ID_NOT_BY_CARD;
- PRINT_WARN("couldn't get a unique id from the card on device "
- "%s (result=x%x), using default id. ipv6 "
- "autoconfig on other lpars may lead to duplicate "
- "ip addresses. please use manually "
- "configured ones.\n",
- CARD_BUS_ID(card), cmd->hdr.return_code);
+ dev_warn(&card->gdev->dev, "The network adapter failed to "
+ "generate a unique ID\n");
}
return 0;
}
@@ -3086,9 +3096,8 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode)
if (rc) {
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
if (rc == 0xe080) {
- PRINT_WARN("LAN on card %s if offline! "
- "Waiting for STARTLAN from card.\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "The LAN is offline\n");
card->lan_online = 0;
}
return rc;
@@ -3194,8 +3203,8 @@ static int qeth_l3_recover(void *ptr)
if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
return 0;
QETH_DBF_TEXT(TRACE, 2, "recover2");
- PRINT_WARN("Recovery of device %s started ...\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev,
+ "A recovery process has been started for the device\n");
card->use_hard_stop = 1;
__qeth_l3_set_offline(card->gdev, 1);
rc = __qeth_l3_set_online(card->gdev, 1);
@@ -3203,14 +3212,14 @@ static int qeth_l3_recover(void *ptr)
qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD);
qeth_clear_thread_running_bit(card, QETH_RECOVER_THREAD);
if (!rc)
- PRINT_INFO("Device %s successfully recovered!\n",
- CARD_BUS_ID(card));
+ dev_info(&card->gdev->dev,
+ "Device successfully recovered!\n");
else {
rtnl_lock();
dev_close(card->dev);
rtnl_unlock();
- PRINT_INFO("Device %s could not be recovered!\n",
- CARD_BUS_ID(card));
+ dev_warn(&card->gdev->dev, "The qeth device driver "
+ "failed to recover an error on the device\n");
}
return 0;
}
@@ -3344,7 +3353,7 @@ static int qeth_l3_register_notifiers(void)
return rc;
}
#else
- PRINT_WARN("layer 3 discipline no IPv6 support\n");
+ pr_warning("There is no IPv6 support for the layer 3 discipline\n");
#endif
return 0;
}
@@ -3363,7 +3372,7 @@ static int __init qeth_l3_init(void)
{
int rc = 0;
- PRINT_INFO("register layer 3 discipline\n");
+ pr_info("register layer 3 discipline\n");
rc = qeth_l3_register_notifiers();
return rc;
}
@@ -3371,7 +3380,7 @@ static int __init qeth_l3_init(void)
static void __exit qeth_l3_exit(void)
{
qeth_l3_unregister_notifiers();
- PRINT_INFO("unregister layer 3 discipline\n");
+ pr_info("unregister layer 3 discipline\n");
}
module_init(qeth_l3_init);
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 3d4e3e3f3fc..e529b55b3ce 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -25,9 +25,15 @@
* Sven Schuetz
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/miscdevice.h>
+#include <linux/seq_file.h>
#include "zfcp_ext.h"
+#define ZFCP_BUS_ID_SIZE 20
+
static char *device;
MODULE_AUTHOR("IBM Deutschland Entwicklung GmbH - linux390@de.ibm.com");
@@ -83,9 +89,9 @@ static int __init zfcp_device_setup(char *devstr)
strcpy(str, devstr);
token = strsep(&str, ",");
- if (!token || strlen(token) >= BUS_ID_SIZE)
+ if (!token || strlen(token) >= ZFCP_BUS_ID_SIZE)
goto err_out;
- strncpy(zfcp_data.init_busid, token, BUS_ID_SIZE);
+ strncpy(zfcp_data.init_busid, token, ZFCP_BUS_ID_SIZE);
token = strsep(&str, ",");
if (!token || strict_strtoull(token, 0,
@@ -102,7 +108,7 @@ static int __init zfcp_device_setup(char *devstr)
err_out:
kfree(str);
- pr_err("zfcp: %s is not a valid SCSI device\n", devstr);
+ pr_err("%s is not a valid SCSI device\n", devstr);
return 0;
}
@@ -186,13 +192,13 @@ static int __init zfcp_module_init(void)
retval = misc_register(&zfcp_cfdc_misc);
if (retval) {
- pr_err("zfcp: Registering the misc device zfcp_cfdc failed\n");
+ pr_err("Registering the misc device zfcp_cfdc failed\n");
goto out_misc;
}
retval = zfcp_ccw_register();
if (retval) {
- pr_err("zfcp: The zfcp device driver could not register with "
+ pr_err("The zfcp device driver could not register with "
"the common I/O layer\n");
goto out_ccw_register;
}
@@ -436,6 +442,16 @@ static void _zfcp_status_read_scheduler(struct work_struct *work)
stat_work));
}
+static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
+{
+ struct zfcp_adapter *adapter =
+ container_of(sl, struct zfcp_adapter, service_level);
+
+ seq_printf(m, "zfcp: %s microcode level %x\n",
+ dev_name(&adapter->ccw_device->dev),
+ adapter->fsf_lic_version);
+}
+
/**
* zfcp_adapter_enqueue - enqueue a new adapter to the list
* @ccw_device: pointer to the struct cc_device
@@ -500,6 +516,8 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later);
+ adapter->service_level.seq_print = zfcp_print_sl;
+
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index 951a8d409d1..728147131e1 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
/**
diff --git a/drivers/s390/scsi/zfcp_cfdc.c b/drivers/s390/scsi/zfcp_cfdc.c
index ec2abceca6d..f1a7518e67e 100644
--- a/drivers/s390/scsi/zfcp_cfdc.c
+++ b/drivers/s390/scsi/zfcp_cfdc.c
@@ -7,6 +7,9 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <asm/ccwdev.h>
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index 31012d58cfb..735d675623f 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/ctype.h>
#include <asm/debug.h>
#include "zfcp_ext.h"
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 9ce4c75bd19..e19e46ae4a6 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -33,6 +33,7 @@
#include <asm/qdio.h>
#include <asm/debug.h>
#include <asm/ebcdic.h>
+#include <asm/sysinfo.h>
#include "zfcp_dbf.h"
#include "zfcp_fsf.h"
@@ -515,6 +516,7 @@ struct zfcp_adapter {
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
struct work_struct scan_work;
+ struct service_level service_level;
atomic_t qdio_outb_full; /* queue full incidents */
};
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index c557ba34e1a..4ed4950d994 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#define ZFCP_MAX_ERPS 3
@@ -1281,10 +1284,13 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
break;
case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
- if (result != ZFCP_ERP_SUCCEEDED)
+ if (result != ZFCP_ERP_SUCCEEDED) {
+ unregister_service_level(&adapter->service_level);
zfcp_erp_rports_del(adapter);
- else
+ } else {
+ register_service_level(&adapter->service_level);
schedule_work(&adapter->scan_work);
+ }
zfcp_adapter_put(adapter);
break;
}
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 8aab3091a7b..f009f2a7ec3 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
struct ct_iu_gpn_ft_req {
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index dc036769040..9c72e083559 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include <linux/blktrace_api.h>
#include "zfcp_ext.h"
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 664752f90b2..d3b55fb66f1 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
/* FIXME(tune): free space should be one max. SBAL chain plus what? */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 468c880f8b6..9dc42a68fbd 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2002, 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#include <asm/atomic.h>
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index ca9293ba176..899af2b45b1 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -6,6 +6,9 @@
* Copyright IBM Corporation 2008
*/
+#define KMSG_COMPONENT "zfcp"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
#include "zfcp_ext.h"
#define ZFCP_DEV_ATTR(_feat, _name, _mode, _show, _store) \
diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c
index c3e4ab07b9c..0eea9078138 100644
--- a/drivers/s390/sysinfo.c
+++ b/drivers/s390/sysinfo.c
@@ -1,17 +1,21 @@
/*
* drivers/s390/sysinfo.c
*
- * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ * Copyright IBM Corp. 2001, 2008
+ * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <asm/ebcdic.h>
#include <asm/sysinfo.h>
+#include <asm/cpcmd.h>
/* Sigh, math-emu. Don't ask. */
#include <asm/sfp-util.h>
@@ -271,6 +275,125 @@ static __init int create_proc_sysinfo(void)
__initcall(create_proc_sysinfo);
+/*
+ * Service levels interface.
+ */
+
+static DECLARE_RWSEM(service_level_sem);
+static LIST_HEAD(service_level_list);
+
+int register_service_level(struct service_level *slr)
+{
+ struct service_level *ptr;
+
+ down_write(&service_level_sem);
+ list_for_each_entry(ptr, &service_level_list, list)
+ if (ptr == slr) {
+ up_write(&service_level_sem);
+ return -EEXIST;
+ }
+ list_add_tail(&slr->list, &service_level_list);
+ up_write(&service_level_sem);
+ return 0;
+}
+EXPORT_SYMBOL(register_service_level);
+
+int unregister_service_level(struct service_level *slr)
+{
+ struct service_level *ptr, *next;
+ int rc = -ENOENT;
+
+ down_write(&service_level_sem);
+ list_for_each_entry_safe(ptr, next, &service_level_list, list) {
+ if (ptr != slr)
+ continue;
+ list_del(&ptr->list);
+ rc = 0;
+ break;
+ }
+ up_write(&service_level_sem);
+ return rc;
+}
+EXPORT_SYMBOL(unregister_service_level);
+
+static void *service_level_start(struct seq_file *m, loff_t *pos)
+{
+ down_read(&service_level_sem);
+ return seq_list_start(&service_level_list, *pos);
+}
+
+static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ return seq_list_next(p, &service_level_list, pos);
+}
+
+static void service_level_stop(struct seq_file *m, void *p)
+{
+ up_read(&service_level_sem);
+}
+
+static int service_level_show(struct seq_file *m, void *p)
+{
+ struct service_level *slr;
+
+ slr = list_entry(p, struct service_level, list);
+ slr->seq_print(m, slr);
+ return 0;
+}
+
+static const struct seq_operations service_level_seq_ops = {
+ .start = service_level_start,
+ .next = service_level_next,
+ .stop = service_level_stop,
+ .show = service_level_show
+};
+
+static int service_level_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &service_level_seq_ops);
+}
+
+static const struct file_operations service_level_ops = {
+ .open = service_level_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+static void service_level_vm_print(struct seq_file *m,
+ struct service_level *slr)
+{
+ char *query_buffer, *str;
+
+ query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
+ if (!query_buffer)
+ return;
+ cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
+ str = strchr(query_buffer, '\n');
+ if (str)
+ *str = 0;
+ seq_printf(m, "VM: %s\n", query_buffer);
+ kfree(query_buffer);
+}
+
+static struct service_level service_level_vm = {
+ .seq_print = service_level_vm_print
+};
+
+static __init int create_proc_service_level(void)
+{
+ proc_create("service_levels", 0, NULL, &service_level_ops);
+ if (MACHINE_IS_VM)
+ register_service_level(&service_level_vm);
+ return 0;
+}
+
+subsys_initcall(create_proc_service_level);
+
+/*
+ * Bogomips calculation based on cpu capability.
+ */
+
int get_cpu_capability(unsigned int *capability)
{
struct sysinfo_1_2_2 *info;