xref: /linux/drivers/s390/char/monreader.c (revision 552c69b36ebd966186573b9c7a286b390935cce1)
16f05e69eSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Character device driver for reading z/VM *MONITOR service records.
41da177e4SLinus Torvalds  *
52b1e3e55SGerald Schaefer  * Copyright IBM Corp. 2004, 2009
62b1e3e55SGerald Schaefer  *
72ca5b6e2SGerald Schaefer  * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10a4f5a299SGerald Schaefer #define KMSG_COMPONENT "monreader"
11a4f5a299SGerald Schaefer #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12a4f5a299SGerald Schaefer 
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/moduleparam.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/kernel.h>
191da177e4SLinus Torvalds #include <linux/miscdevice.h>
201da177e4SLinus Torvalds #include <linux/ctype.h>
211da177e4SLinus Torvalds #include <linux/spinlock.h>
221da177e4SLinus Torvalds #include <linux/interrupt.h>
232ca5b6e2SGerald Schaefer #include <linux/poll.h>
242b1e3e55SGerald Schaefer #include <linux/device.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
262ca5b6e2SGerald Schaefer #include <net/iucv/iucv.h>
277c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
281da177e4SLinus Torvalds #include <asm/ebcdic.h>
291da177e4SLinus Torvalds #include <asm/extmem.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds #define MON_COLLECT_SAMPLE 0x80
331da177e4SLinus Torvalds #define MON_COLLECT_EVENT  0x40
341da177e4SLinus Torvalds #define MON_SERVICE	   "*MONITOR"
351da177e4SLinus Torvalds #define MON_IN_USE	   0x01
361da177e4SLinus Torvalds #define MON_MSGLIM	   255
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds static char mon_dcss_name[9] = "MONDCSS\0";
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds struct mon_msg {
411da177e4SLinus Torvalds 	u32 pos;
421da177e4SLinus Torvalds 	u32 mca_offset;
43c667aac8SMartin Schwidefsky 	struct iucv_message msg;
441da177e4SLinus Torvalds 	char msglim_reached;
451da177e4SLinus Torvalds 	char replied_msglim;
461da177e4SLinus Torvalds };
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds struct mon_private {
49c667aac8SMartin Schwidefsky 	struct iucv_path *path;
501da177e4SLinus Torvalds 	struct mon_msg *msg_array[MON_MSGLIM];
511da177e4SLinus Torvalds 	unsigned int   write_index;
521da177e4SLinus Torvalds 	unsigned int   read_index;
531da177e4SLinus Torvalds 	atomic_t msglim_count;
541da177e4SLinus Torvalds 	atomic_t read_ready;
551da177e4SLinus Torvalds 	atomic_t iucv_connected;
561da177e4SLinus Torvalds 	atomic_t iucv_severed;
571da177e4SLinus Torvalds };
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds static unsigned long mon_in_use = 0;
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds static unsigned long mon_dcss_start;
621da177e4SLinus Torvalds static unsigned long mon_dcss_end;
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(mon_read_wait_queue);
651da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(mon_conn_wait_queue);
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds static u8 user_data_connect[16] = {
681da177e4SLinus Torvalds 	/* Version code, must be 0x01 for shared mode */
691da177e4SLinus Torvalds 	0x01,
701da177e4SLinus Torvalds 	/* what to collect */
711da177e4SLinus Torvalds 	MON_COLLECT_SAMPLE | MON_COLLECT_EVENT,
721da177e4SLinus Torvalds 	/* DCSS name in EBCDIC, 8 bytes padded with blanks */
731da177e4SLinus Torvalds 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
741da177e4SLinus Torvalds 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
751da177e4SLinus Torvalds };
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds static u8 user_data_sever[16] = {
781da177e4SLinus Torvalds 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
791da177e4SLinus Torvalds 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
801da177e4SLinus Torvalds };
811da177e4SLinus Torvalds 
822b1e3e55SGerald Schaefer static struct device *monreader_device;
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds /******************************************************************************
851da177e4SLinus Torvalds  *                             helper functions                               *
861da177e4SLinus Torvalds  *****************************************************************************/
871da177e4SLinus Torvalds /*
881da177e4SLinus Torvalds  * Create the 8 bytes EBCDIC DCSS segment name from
891da177e4SLinus Torvalds  * an ASCII name, incl. padding
901da177e4SLinus Torvalds  */
919b0c455aSMartin Schwidefsky static void dcss_mkname(char *ascii_name, char *ebcdic_name)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	int i;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	for (i = 0; i < 8; i++) {
961da177e4SLinus Torvalds 		if (ascii_name[i] == '\0')
971da177e4SLinus Torvalds 			break;
981da177e4SLinus Torvalds 		ebcdic_name[i] = toupper(ascii_name[i]);
993b974874SPeter Senna Tschudin 	}
1001da177e4SLinus Torvalds 	for (; i < 8; i++)
1011da177e4SLinus Torvalds 		ebcdic_name[i] = ' ';
1021da177e4SLinus Torvalds 	ASCEBC(ebcdic_name, 8);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
105c667aac8SMartin Schwidefsky static inline unsigned long mon_mca_start(struct mon_msg *monmsg)
1061da177e4SLinus Torvalds {
107c667aac8SMartin Schwidefsky 	return *(u32 *) &monmsg->msg.rmmsg;
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
110c667aac8SMartin Schwidefsky static inline unsigned long mon_mca_end(struct mon_msg *monmsg)
1111da177e4SLinus Torvalds {
112c667aac8SMartin Schwidefsky 	return *(u32 *) &monmsg->msg.rmmsg[4];
1131da177e4SLinus Torvalds }
1141da177e4SLinus Torvalds 
115c667aac8SMartin Schwidefsky static inline u8 mon_mca_type(struct mon_msg *monmsg, u8 index)
1161da177e4SLinus Torvalds {
1171da177e4SLinus Torvalds 	return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index);
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
120c667aac8SMartin Schwidefsky static inline u32 mon_mca_size(struct mon_msg *monmsg)
1211da177e4SLinus Torvalds {
1221da177e4SLinus Torvalds 	return mon_mca_end(monmsg) - mon_mca_start(monmsg) + 1;
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
125c667aac8SMartin Schwidefsky static inline u32 mon_rec_start(struct mon_msg *monmsg)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4));
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
130c667aac8SMartin Schwidefsky static inline u32 mon_rec_end(struct mon_msg *monmsg)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8));
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1359b0c455aSMartin Schwidefsky static int mon_check_mca(struct mon_msg *monmsg)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds 	if ((mon_rec_end(monmsg) <= mon_rec_start(monmsg)) ||
1381da177e4SLinus Torvalds 	    (mon_rec_start(monmsg) < mon_dcss_start) ||
1391da177e4SLinus Torvalds 	    (mon_rec_end(monmsg) > mon_dcss_end) ||
1401da177e4SLinus Torvalds 	    (mon_mca_type(monmsg, 0) == 0) ||
1411da177e4SLinus Torvalds 	    (mon_mca_size(monmsg) % 12 != 0) ||
1421da177e4SLinus Torvalds 	    (mon_mca_end(monmsg) <= mon_mca_start(monmsg)) ||
1431da177e4SLinus Torvalds 	    (mon_mca_end(monmsg) > mon_dcss_end) ||
1441da177e4SLinus Torvalds 	    (mon_mca_start(monmsg) < mon_dcss_start) ||
1451da177e4SLinus Torvalds 	    ((mon_mca_type(monmsg, 1) == 0) && (mon_mca_type(monmsg, 2) == 0)))
1461da177e4SLinus Torvalds 		return -EINVAL;
1471da177e4SLinus Torvalds 	return 0;
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds 
1509b0c455aSMartin Schwidefsky static int mon_send_reply(struct mon_msg *monmsg,
151c667aac8SMartin Schwidefsky 			  struct mon_private *monpriv)
1521da177e4SLinus Torvalds {
1531da177e4SLinus Torvalds 	int rc;
1541da177e4SLinus Torvalds 
155c667aac8SMartin Schwidefsky 	rc = iucv_message_reply(monpriv->path, &monmsg->msg,
156c667aac8SMartin Schwidefsky 				IUCV_IPRMDATA, NULL, 0);
1571da177e4SLinus Torvalds 	atomic_dec(&monpriv->msglim_count);
1581da177e4SLinus Torvalds 	if (likely(!monmsg->msglim_reached)) {
1591da177e4SLinus Torvalds 		monmsg->pos = 0;
1601da177e4SLinus Torvalds 		monmsg->mca_offset = 0;
1611da177e4SLinus Torvalds 		monpriv->read_index = (monpriv->read_index + 1) %
1621da177e4SLinus Torvalds 				      MON_MSGLIM;
1631da177e4SLinus Torvalds 		atomic_dec(&monpriv->read_ready);
1641da177e4SLinus Torvalds 	} else
1651da177e4SLinus Torvalds 		monmsg->replied_msglim = 1;
1661da177e4SLinus Torvalds 	if (rc) {
167a4f5a299SGerald Schaefer 		pr_err("Reading monitor data failed with rc=%i\n", rc);
1681da177e4SLinus Torvalds 		return -EIO;
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 	return 0;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
1739b0c455aSMartin Schwidefsky static void mon_free_mem(struct mon_private *monpriv)
1741da177e4SLinus Torvalds {
175c667aac8SMartin Schwidefsky 	int i;
176c667aac8SMartin Schwidefsky 
177c667aac8SMartin Schwidefsky 	for (i = 0; i < MON_MSGLIM; i++)
178c667aac8SMartin Schwidefsky 		kfree(monpriv->msg_array[i]);
179c667aac8SMartin Schwidefsky 	kfree(monpriv);
180c667aac8SMartin Schwidefsky }
181c667aac8SMartin Schwidefsky 
1829b0c455aSMartin Schwidefsky static struct mon_private *mon_alloc_mem(void)
183c667aac8SMartin Schwidefsky {
184c667aac8SMartin Schwidefsky 	int i;
1851da177e4SLinus Torvalds 	struct mon_private *monpriv;
1861da177e4SLinus Torvalds 
18788abaab4SEric Sesterhenn 	monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
1882ca5b6e2SGerald Schaefer 	if (!monpriv)
1891da177e4SLinus Torvalds 		return NULL;
1901da177e4SLinus Torvalds 	for (i = 0; i < MON_MSGLIM; i++) {
19188abaab4SEric Sesterhenn 		monpriv->msg_array[i] = kzalloc(sizeof(struct mon_msg),
1921da177e4SLinus Torvalds 						    GFP_KERNEL);
1931da177e4SLinus Torvalds 		if (!monpriv->msg_array[i]) {
194c667aac8SMartin Schwidefsky 			mon_free_mem(monpriv);
1951da177e4SLinus Torvalds 			return NULL;
1961da177e4SLinus Torvalds 		}
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 	return monpriv;
1991da177e4SLinus Torvalds }
2001da177e4SLinus Torvalds 
201c667aac8SMartin Schwidefsky static inline void mon_next_mca(struct mon_msg *monmsg)
2021da177e4SLinus Torvalds {
2031da177e4SLinus Torvalds 	if (likely((mon_mca_size(monmsg) - monmsg->mca_offset) == 12))
2041da177e4SLinus Torvalds 		return;
2051da177e4SLinus Torvalds 	monmsg->mca_offset += 12;
2061da177e4SLinus Torvalds 	monmsg->pos = 0;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
2099b0c455aSMartin Schwidefsky static struct mon_msg *mon_next_message(struct mon_private *monpriv)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	struct mon_msg *monmsg;
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	if (!atomic_read(&monpriv->read_ready))
2141da177e4SLinus Torvalds 		return NULL;
2151da177e4SLinus Torvalds 	monmsg = monpriv->msg_array[monpriv->read_index];
2161da177e4SLinus Torvalds 	if (unlikely(monmsg->replied_msglim)) {
2171da177e4SLinus Torvalds 		monmsg->replied_msglim = 0;
2181da177e4SLinus Torvalds 		monmsg->msglim_reached = 0;
2191da177e4SLinus Torvalds 		monmsg->pos = 0;
2201da177e4SLinus Torvalds 		monmsg->mca_offset = 0;
2211da177e4SLinus Torvalds 		monpriv->read_index = (monpriv->read_index + 1) %
2221da177e4SLinus Torvalds 				      MON_MSGLIM;
2231da177e4SLinus Torvalds 		atomic_dec(&monpriv->read_ready);
2241da177e4SLinus Torvalds 		return ERR_PTR(-EOVERFLOW);
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds 	return monmsg;
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds /******************************************************************************
2311da177e4SLinus Torvalds  *                               IUCV handler                                 *
2321da177e4SLinus Torvalds  *****************************************************************************/
23391e60eb6SUrsula Braun static void mon_iucv_path_complete(struct iucv_path *path, u8 *ipuser)
2341da177e4SLinus Torvalds {
235c667aac8SMartin Schwidefsky 	struct mon_private *monpriv = path->private;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	atomic_set(&monpriv->iucv_connected, 1);
2381da177e4SLinus Torvalds 	wake_up(&mon_conn_wait_queue);
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds 
24191e60eb6SUrsula Braun static void mon_iucv_path_severed(struct iucv_path *path, u8 *ipuser)
2421da177e4SLinus Torvalds {
243c667aac8SMartin Schwidefsky 	struct mon_private *monpriv = path->private;
2441da177e4SLinus Torvalds 
245a4f5a299SGerald Schaefer 	pr_err("z/VM *MONITOR system service disconnected with rc=%i\n",
246a4f5a299SGerald Schaefer 	       ipuser[0]);
247c667aac8SMartin Schwidefsky 	iucv_path_sever(path, NULL);
2481da177e4SLinus Torvalds 	atomic_set(&monpriv->iucv_severed, 1);
2491da177e4SLinus Torvalds 	wake_up(&mon_conn_wait_queue);
2501da177e4SLinus Torvalds 	wake_up_interruptible(&mon_read_wait_queue);
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds 
253c667aac8SMartin Schwidefsky static void mon_iucv_message_pending(struct iucv_path *path,
254c667aac8SMartin Schwidefsky 				     struct iucv_message *msg)
2551da177e4SLinus Torvalds {
256c667aac8SMartin Schwidefsky 	struct mon_private *monpriv = path->private;
2571da177e4SLinus Torvalds 
258c667aac8SMartin Schwidefsky 	memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
259c667aac8SMartin Schwidefsky 	       msg, sizeof(*msg));
2601da177e4SLinus Torvalds 	if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
261baebc70aSJoe Perches 		pr_warn("The read queue for monitor data is full\n");
2621da177e4SLinus Torvalds 		monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
2631da177e4SLinus Torvalds 	}
2641da177e4SLinus Torvalds 	monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
2651da177e4SLinus Torvalds 	atomic_inc(&monpriv->read_ready);
2661da177e4SLinus Torvalds 	wake_up_interruptible(&mon_read_wait_queue);
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
269c667aac8SMartin Schwidefsky static struct iucv_handler monreader_iucv_handler = {
270c667aac8SMartin Schwidefsky 	.path_complete	 = mon_iucv_path_complete,
271c667aac8SMartin Schwidefsky 	.path_severed	 = mon_iucv_path_severed,
272c667aac8SMartin Schwidefsky 	.message_pending = mon_iucv_message_pending,
2731da177e4SLinus Torvalds };
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds /******************************************************************************
2761da177e4SLinus Torvalds  *                               file operations                              *
2771da177e4SLinus Torvalds  *****************************************************************************/
278c667aac8SMartin Schwidefsky static int mon_open(struct inode *inode, struct file *filp)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	struct mon_private *monpriv;
281c667aac8SMartin Schwidefsky 	int rc;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	/*
2841da177e4SLinus Torvalds 	 * only one user allowed
2851da177e4SLinus Torvalds 	 */
286c667aac8SMartin Schwidefsky 	rc = -EBUSY;
2871da177e4SLinus Torvalds 	if (test_and_set_bit(MON_IN_USE, &mon_in_use))
288c667aac8SMartin Schwidefsky 		goto out;
2891da177e4SLinus Torvalds 
290c667aac8SMartin Schwidefsky 	rc = -ENOMEM;
2911da177e4SLinus Torvalds 	monpriv = mon_alloc_mem();
2921da177e4SLinus Torvalds 	if (!monpriv)
293c667aac8SMartin Schwidefsky 		goto out_use;
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	/*
296c667aac8SMartin Schwidefsky 	 * Connect to *MONITOR service
2971da177e4SLinus Torvalds 	 */
298c667aac8SMartin Schwidefsky 	monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
299c667aac8SMartin Schwidefsky 	if (!monpriv->path)
300c667aac8SMartin Schwidefsky 		goto out_priv;
301c667aac8SMartin Schwidefsky 	rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
302c667aac8SMartin Schwidefsky 			       MON_SERVICE, NULL, user_data_connect, monpriv);
3031da177e4SLinus Torvalds 	if (rc) {
304a4f5a299SGerald Schaefer 		pr_err("Connecting to the z/VM *MONITOR system service "
305a4f5a299SGerald Schaefer 		       "failed with rc=%i\n", rc);
3061da177e4SLinus Torvalds 		rc = -EIO;
307c667aac8SMartin Schwidefsky 		goto out_path;
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 	/*
3101da177e4SLinus Torvalds 	 * Wait for connection confirmation
3111da177e4SLinus Torvalds 	 */
3121da177e4SLinus Torvalds 	wait_event(mon_conn_wait_queue,
3131da177e4SLinus Torvalds 		   atomic_read(&monpriv->iucv_connected) ||
3141da177e4SLinus Torvalds 		   atomic_read(&monpriv->iucv_severed));
3151da177e4SLinus Torvalds 	if (atomic_read(&monpriv->iucv_severed)) {
3161da177e4SLinus Torvalds 		atomic_set(&monpriv->iucv_severed, 0);
3171da177e4SLinus Torvalds 		atomic_set(&monpriv->iucv_connected, 0);
3181da177e4SLinus Torvalds 		rc = -EIO;
319c667aac8SMartin Schwidefsky 		goto out_path;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 	filp->private_data = monpriv;
32299357742SHeiko Carstens 	dev_set_drvdata(monreader_device, monpriv);
3231da177e4SLinus Torvalds 	return nonseekable_open(inode, filp);
3241da177e4SLinus Torvalds 
325c667aac8SMartin Schwidefsky out_path:
3262b1e3e55SGerald Schaefer 	iucv_path_free(monpriv->path);
327c667aac8SMartin Schwidefsky out_priv:
328c667aac8SMartin Schwidefsky 	mon_free_mem(monpriv);
329c667aac8SMartin Schwidefsky out_use:
3301da177e4SLinus Torvalds 	clear_bit(MON_IN_USE, &mon_in_use);
331c667aac8SMartin Schwidefsky out:
3321da177e4SLinus Torvalds 	return rc;
3331da177e4SLinus Torvalds }
3341da177e4SLinus Torvalds 
335c667aac8SMartin Schwidefsky static int mon_close(struct inode *inode, struct file *filp)
3361da177e4SLinus Torvalds {
3371da177e4SLinus Torvalds 	int rc, i;
3381da177e4SLinus Torvalds 	struct mon_private *monpriv = filp->private_data;
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 	/*
3411da177e4SLinus Torvalds 	 * Close IUCV connection and unregister
3421da177e4SLinus Torvalds 	 */
3432b1e3e55SGerald Schaefer 	if (monpriv->path) {
344c667aac8SMartin Schwidefsky 		rc = iucv_path_sever(monpriv->path, user_data_sever);
3451da177e4SLinus Torvalds 		if (rc)
346baebc70aSJoe Perches 			pr_warn("Disconnecting the z/VM *MONITOR system service failed with rc=%i\n",
347baebc70aSJoe Perches 				rc);
3482b1e3e55SGerald Schaefer 		iucv_path_free(monpriv->path);
3492b1e3e55SGerald Schaefer 	}
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	atomic_set(&monpriv->iucv_severed, 0);
3521da177e4SLinus Torvalds 	atomic_set(&monpriv->iucv_connected, 0);
3531da177e4SLinus Torvalds 	atomic_set(&monpriv->read_ready, 0);
3541da177e4SLinus Torvalds 	atomic_set(&monpriv->msglim_count, 0);
3551da177e4SLinus Torvalds 	monpriv->write_index  = 0;
3561da177e4SLinus Torvalds 	monpriv->read_index   = 0;
357ccaf6553SGerald Schaefer 	dev_set_drvdata(monreader_device, NULL);
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	for (i = 0; i < MON_MSGLIM; i++)
3601da177e4SLinus Torvalds 		kfree(monpriv->msg_array[i]);
3611da177e4SLinus Torvalds 	kfree(monpriv);
3621da177e4SLinus Torvalds 	clear_bit(MON_IN_USE, &mon_in_use);
3631da177e4SLinus Torvalds 	return 0;
3641da177e4SLinus Torvalds }
3651da177e4SLinus Torvalds 
366c667aac8SMartin Schwidefsky static ssize_t mon_read(struct file *filp, char __user *data,
367c667aac8SMartin Schwidefsky 			size_t count, loff_t *ppos)
3681da177e4SLinus Torvalds {
3691da177e4SLinus Torvalds 	struct mon_private *monpriv = filp->private_data;
3701da177e4SLinus Torvalds 	struct mon_msg *monmsg;
3711da177e4SLinus Torvalds 	int ret;
3721da177e4SLinus Torvalds 	u32 mce_start;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	monmsg = mon_next_message(monpriv);
3751da177e4SLinus Torvalds 	if (IS_ERR(monmsg))
3761da177e4SLinus Torvalds 		return PTR_ERR(monmsg);
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	if (!monmsg) {
3791da177e4SLinus Torvalds 		if (filp->f_flags & O_NONBLOCK)
3801da177e4SLinus Torvalds 			return -EAGAIN;
3811da177e4SLinus Torvalds 		ret = wait_event_interruptible(mon_read_wait_queue,
3821da177e4SLinus Torvalds 					atomic_read(&monpriv->read_ready) ||
3831da177e4SLinus Torvalds 					atomic_read(&monpriv->iucv_severed));
3841da177e4SLinus Torvalds 		if (ret)
3851da177e4SLinus Torvalds 			return ret;
3861da177e4SLinus Torvalds 		if (unlikely(atomic_read(&monpriv->iucv_severed)))
3871da177e4SLinus Torvalds 			return -EIO;
3881da177e4SLinus Torvalds 		monmsg = monpriv->msg_array[monpriv->read_index];
3891da177e4SLinus Torvalds 	}
3901da177e4SLinus Torvalds 
3912ca5b6e2SGerald Schaefer 	if (!monmsg->pos)
3921da177e4SLinus Torvalds 		monmsg->pos = mon_mca_start(monmsg) + monmsg->mca_offset;
3931da177e4SLinus Torvalds 	if (mon_check_mca(monmsg))
3941da177e4SLinus Torvalds 		goto reply;
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	/* read monitor control element (12 bytes) first */
3971da177e4SLinus Torvalds 	mce_start = mon_mca_start(monmsg) + monmsg->mca_offset;
3981da177e4SLinus Torvalds 	if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) {
3991da177e4SLinus Torvalds 		count = min(count, (size_t) mce_start + 12 - monmsg->pos);
4001da177e4SLinus Torvalds 		ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
4011da177e4SLinus Torvalds 				   count);
4021da177e4SLinus Torvalds 		if (ret)
4031da177e4SLinus Torvalds 			return -EFAULT;
4041da177e4SLinus Torvalds 		monmsg->pos += count;
4051da177e4SLinus Torvalds 		if (monmsg->pos == mce_start + 12)
4061da177e4SLinus Torvalds 			monmsg->pos = mon_rec_start(monmsg);
4071da177e4SLinus Torvalds 		goto out_copy;
4081da177e4SLinus Torvalds 	}
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	/* read records */
4111da177e4SLinus Torvalds 	if (monmsg->pos <= mon_rec_end(monmsg)) {
4121da177e4SLinus Torvalds 		count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos
4131da177e4SLinus Torvalds 					    + 1);
4141da177e4SLinus Torvalds 		ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos,
4151da177e4SLinus Torvalds 				   count);
4161da177e4SLinus Torvalds 		if (ret)
4171da177e4SLinus Torvalds 			return -EFAULT;
4181da177e4SLinus Torvalds 		monmsg->pos += count;
4191da177e4SLinus Torvalds 		if (monmsg->pos > mon_rec_end(monmsg))
4201da177e4SLinus Torvalds 			mon_next_mca(monmsg);
4211da177e4SLinus Torvalds 		goto out_copy;
4221da177e4SLinus Torvalds 	}
4231da177e4SLinus Torvalds reply:
4241da177e4SLinus Torvalds 	ret = mon_send_reply(monmsg, monpriv);
4251da177e4SLinus Torvalds 	return ret;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds out_copy:
4281da177e4SLinus Torvalds 	*ppos += count;
4291da177e4SLinus Torvalds 	return count;
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds 
432afc9a42bSAl Viro static __poll_t mon_poll(struct file *filp, struct poll_table_struct *p)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds 	struct mon_private *monpriv = filp->private_data;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	poll_wait(filp, &mon_read_wait_queue, p);
4371da177e4SLinus Torvalds 	if (unlikely(atomic_read(&monpriv->iucv_severed)))
438*a9a08845SLinus Torvalds 		return EPOLLERR;
4391da177e4SLinus Torvalds 	if (atomic_read(&monpriv->read_ready))
440*a9a08845SLinus Torvalds 		return EPOLLIN | EPOLLRDNORM;
4411da177e4SLinus Torvalds 	return 0;
4421da177e4SLinus Torvalds }
4431da177e4SLinus Torvalds 
444d54b1fdbSArjan van de Ven static const struct file_operations mon_fops = {
4451da177e4SLinus Torvalds 	.owner   = THIS_MODULE,
4461da177e4SLinus Torvalds 	.open    = &mon_open,
4471da177e4SLinus Torvalds 	.release = &mon_close,
4481da177e4SLinus Torvalds 	.read    = &mon_read,
4491da177e4SLinus Torvalds 	.poll    = &mon_poll,
4506038f373SArnd Bergmann 	.llseek  = noop_llseek,
4511da177e4SLinus Torvalds };
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds static struct miscdevice mon_dev = {
4541da177e4SLinus Torvalds 	.name       = "monreader",
4551da177e4SLinus Torvalds 	.fops       = &mon_fops,
4561da177e4SLinus Torvalds 	.minor      = MISC_DYNAMIC_MINOR,
4571da177e4SLinus Torvalds };
4581da177e4SLinus Torvalds 
4592b1e3e55SGerald Schaefer 
4602b1e3e55SGerald Schaefer /******************************************************************************
4612b1e3e55SGerald Schaefer  *				suspend / resume			      *
4622b1e3e55SGerald Schaefer  *****************************************************************************/
4632b1e3e55SGerald Schaefer static int monreader_freeze(struct device *dev)
4642b1e3e55SGerald Schaefer {
46599357742SHeiko Carstens 	struct mon_private *monpriv = dev_get_drvdata(dev);
4662b1e3e55SGerald Schaefer 	int rc;
4672b1e3e55SGerald Schaefer 
4682b1e3e55SGerald Schaefer 	if (!monpriv)
4692b1e3e55SGerald Schaefer 		return 0;
4702b1e3e55SGerald Schaefer 	if (monpriv->path) {
4712b1e3e55SGerald Schaefer 		rc = iucv_path_sever(monpriv->path, user_data_sever);
4722b1e3e55SGerald Schaefer 		if (rc)
473baebc70aSJoe Perches 			pr_warn("Disconnecting the z/VM *MONITOR system service failed with rc=%i\n",
474baebc70aSJoe Perches 				rc);
4752b1e3e55SGerald Schaefer 		iucv_path_free(monpriv->path);
4762b1e3e55SGerald Schaefer 	}
4772b1e3e55SGerald Schaefer 	atomic_set(&monpriv->iucv_severed, 0);
4782b1e3e55SGerald Schaefer 	atomic_set(&monpriv->iucv_connected, 0);
4792b1e3e55SGerald Schaefer 	atomic_set(&monpriv->read_ready, 0);
4802b1e3e55SGerald Schaefer 	atomic_set(&monpriv->msglim_count, 0);
4812b1e3e55SGerald Schaefer 	monpriv->write_index  = 0;
4822b1e3e55SGerald Schaefer 	monpriv->read_index   = 0;
4832b1e3e55SGerald Schaefer 	monpriv->path = NULL;
4842b1e3e55SGerald Schaefer 	return 0;
4852b1e3e55SGerald Schaefer }
4862b1e3e55SGerald Schaefer 
4872b1e3e55SGerald Schaefer static int monreader_thaw(struct device *dev)
4882b1e3e55SGerald Schaefer {
4894f0076f7SMartin Schwidefsky 	struct mon_private *monpriv = dev_get_drvdata(dev);
4902b1e3e55SGerald Schaefer 	int rc;
4912b1e3e55SGerald Schaefer 
4922b1e3e55SGerald Schaefer 	if (!monpriv)
4932b1e3e55SGerald Schaefer 		return 0;
4942b1e3e55SGerald Schaefer 	rc = -ENOMEM;
4952b1e3e55SGerald Schaefer 	monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
4962b1e3e55SGerald Schaefer 	if (!monpriv->path)
4972b1e3e55SGerald Schaefer 		goto out;
4982b1e3e55SGerald Schaefer 	rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
4992b1e3e55SGerald Schaefer 			       MON_SERVICE, NULL, user_data_connect, monpriv);
5002b1e3e55SGerald Schaefer 	if (rc) {
5012b1e3e55SGerald Schaefer 		pr_err("Connecting to the z/VM *MONITOR system service "
5022b1e3e55SGerald Schaefer 		       "failed with rc=%i\n", rc);
5032b1e3e55SGerald Schaefer 		goto out_path;
5042b1e3e55SGerald Schaefer 	}
5052b1e3e55SGerald Schaefer 	wait_event(mon_conn_wait_queue,
5062b1e3e55SGerald Schaefer 		   atomic_read(&monpriv->iucv_connected) ||
5072b1e3e55SGerald Schaefer 		   atomic_read(&monpriv->iucv_severed));
5082b1e3e55SGerald Schaefer 	if (atomic_read(&monpriv->iucv_severed))
5092b1e3e55SGerald Schaefer 		goto out_path;
5102b1e3e55SGerald Schaefer 	return 0;
5112b1e3e55SGerald Schaefer out_path:
5122b1e3e55SGerald Schaefer 	rc = -EIO;
5132b1e3e55SGerald Schaefer 	iucv_path_free(monpriv->path);
5142b1e3e55SGerald Schaefer 	monpriv->path = NULL;
5152b1e3e55SGerald Schaefer out:
5162b1e3e55SGerald Schaefer 	atomic_set(&monpriv->iucv_severed, 1);
5172b1e3e55SGerald Schaefer 	return rc;
5182b1e3e55SGerald Schaefer }
5192b1e3e55SGerald Schaefer 
5202b1e3e55SGerald Schaefer static int monreader_restore(struct device *dev)
5212b1e3e55SGerald Schaefer {
5222b1e3e55SGerald Schaefer 	int rc;
5232b1e3e55SGerald Schaefer 
5242b1e3e55SGerald Schaefer 	segment_unload(mon_dcss_name);
5252b1e3e55SGerald Schaefer 	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
5262b1e3e55SGerald Schaefer 			  &mon_dcss_start, &mon_dcss_end);
5272b1e3e55SGerald Schaefer 	if (rc < 0) {
5282b1e3e55SGerald Schaefer 		segment_warning(rc, mon_dcss_name);
5292b1e3e55SGerald Schaefer 		panic("fatal monreader resume error: no monitor dcss\n");
5302b1e3e55SGerald Schaefer 	}
5312b1e3e55SGerald Schaefer 	return monreader_thaw(dev);
5322b1e3e55SGerald Schaefer }
5332b1e3e55SGerald Schaefer 
53447145210SAlexey Dobriyan static const struct dev_pm_ops monreader_pm_ops = {
5352b1e3e55SGerald Schaefer 	.freeze  = monreader_freeze,
5362b1e3e55SGerald Schaefer 	.thaw	 = monreader_thaw,
5372b1e3e55SGerald Schaefer 	.restore = monreader_restore,
5382b1e3e55SGerald Schaefer };
5392b1e3e55SGerald Schaefer 
5402b1e3e55SGerald Schaefer static struct device_driver monreader_driver = {
5412b1e3e55SGerald Schaefer 	.name = "monreader",
5422b1e3e55SGerald Schaefer 	.bus  = &iucv_bus,
5432b1e3e55SGerald Schaefer 	.pm   = &monreader_pm_ops,
5442b1e3e55SGerald Schaefer };
5452b1e3e55SGerald Schaefer 
5462b1e3e55SGerald Schaefer 
5471da177e4SLinus Torvalds /******************************************************************************
5481da177e4SLinus Torvalds  *                              module init/exit                              *
5491da177e4SLinus Torvalds  *****************************************************************************/
550c667aac8SMartin Schwidefsky static int __init mon_init(void)
5511da177e4SLinus Torvalds {
5521da177e4SLinus Torvalds 	int rc;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	if (!MACHINE_IS_VM) {
555a4f5a299SGerald Schaefer 		pr_err("The z/VM *MONITOR record device driver cannot be "
556a4f5a299SGerald Schaefer 		       "loaded without z/VM\n");
5571da177e4SLinus Torvalds 		return -ENODEV;
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 
560c667aac8SMartin Schwidefsky 	/*
561c667aac8SMartin Schwidefsky 	 * Register with IUCV and connect to *MONITOR service
562c667aac8SMartin Schwidefsky 	 */
563c667aac8SMartin Schwidefsky 	rc = iucv_register(&monreader_iucv_handler, 1);
564c667aac8SMartin Schwidefsky 	if (rc) {
565a4f5a299SGerald Schaefer 		pr_err("The z/VM *MONITOR record device driver failed to "
566a4f5a299SGerald Schaefer 		       "register with IUCV\n");
567c667aac8SMartin Schwidefsky 		return rc;
568c667aac8SMartin Schwidefsky 	}
569c667aac8SMartin Schwidefsky 
5702b1e3e55SGerald Schaefer 	rc = driver_register(&monreader_driver);
5712b1e3e55SGerald Schaefer 	if (rc)
5722b1e3e55SGerald Schaefer 		goto out_iucv;
5732b1e3e55SGerald Schaefer 	monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
57472abaadfSPeter Senna Tschudin 	if (!monreader_device) {
57572abaadfSPeter Senna Tschudin 		rc = -ENOMEM;
5762b1e3e55SGerald Schaefer 		goto out_driver;
57772abaadfSPeter Senna Tschudin 	}
57872abaadfSPeter Senna Tschudin 
5792b1e3e55SGerald Schaefer 	dev_set_name(monreader_device, "monreader-dev");
5802b1e3e55SGerald Schaefer 	monreader_device->bus = &iucv_bus;
5812b1e3e55SGerald Schaefer 	monreader_device->parent = iucv_root;
5822b1e3e55SGerald Schaefer 	monreader_device->driver = &monreader_driver;
5832b1e3e55SGerald Schaefer 	monreader_device->release = (void (*)(struct device *))kfree;
5842b1e3e55SGerald Schaefer 	rc = device_register(monreader_device);
5852b1e3e55SGerald Schaefer 	if (rc) {
586c6304933SSebastian Ott 		put_device(monreader_device);
5872b1e3e55SGerald Schaefer 		goto out_driver;
5882b1e3e55SGerald Schaefer 	}
5892b1e3e55SGerald Schaefer 
5901da177e4SLinus Torvalds 	rc = segment_type(mon_dcss_name);
5911da177e4SLinus Torvalds 	if (rc < 0) {
592ca68305bSMartin Schwidefsky 		segment_warning(rc, mon_dcss_name);
5932b1e3e55SGerald Schaefer 		goto out_device;
5941da177e4SLinus Torvalds 	}
5951da177e4SLinus Torvalds 	if (rc != SEG_TYPE_SC) {
596a4f5a299SGerald Schaefer 		pr_err("The specified *MONITOR DCSS %s does not have the "
597a4f5a299SGerald Schaefer 		       "required type SC\n", mon_dcss_name);
598c667aac8SMartin Schwidefsky 		rc = -EINVAL;
5992b1e3e55SGerald Schaefer 		goto out_device;
6001da177e4SLinus Torvalds 	}
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
6031da177e4SLinus Torvalds 			  &mon_dcss_start, &mon_dcss_end);
6041da177e4SLinus Torvalds 	if (rc < 0) {
605ca68305bSMartin Schwidefsky 		segment_warning(rc, mon_dcss_name);
606c667aac8SMartin Schwidefsky 		rc = -EINVAL;
6072b1e3e55SGerald Schaefer 		goto out_device;
6081da177e4SLinus Torvalds 	}
6091da177e4SLinus Torvalds 	dcss_mkname(mon_dcss_name, &user_data_connect[8]);
6101da177e4SLinus Torvalds 
6111963403aSGerald Schaefer 	/*
6121963403aSGerald Schaefer 	 * misc_register() has to be the last action in module_init(), because
6131963403aSGerald Schaefer 	 * file operations will be available right after this.
6141963403aSGerald Schaefer 	 */
6151da177e4SLinus Torvalds 	rc = misc_register(&mon_dev);
6162ca5b6e2SGerald Schaefer 	if (rc < 0 )
6171da177e4SLinus Torvalds 		goto out;
6181da177e4SLinus Torvalds 	return 0;
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds out:
6211da177e4SLinus Torvalds 	segment_unload(mon_dcss_name);
6222b1e3e55SGerald Schaefer out_device:
6232b1e3e55SGerald Schaefer 	device_unregister(monreader_device);
6242b1e3e55SGerald Schaefer out_driver:
6252b1e3e55SGerald Schaefer 	driver_unregister(&monreader_driver);
626c667aac8SMartin Schwidefsky out_iucv:
627c667aac8SMartin Schwidefsky 	iucv_unregister(&monreader_iucv_handler, 1);
6281da177e4SLinus Torvalds 	return rc;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
631c667aac8SMartin Schwidefsky static void __exit mon_exit(void)
6321da177e4SLinus Torvalds {
6331da177e4SLinus Torvalds 	segment_unload(mon_dcss_name);
634547415d5SAkinobu Mita 	misc_deregister(&mon_dev);
6352b1e3e55SGerald Schaefer 	device_unregister(monreader_device);
6362b1e3e55SGerald Schaefer 	driver_unregister(&monreader_driver);
637c667aac8SMartin Schwidefsky 	iucv_unregister(&monreader_iucv_handler, 1);
6381da177e4SLinus Torvalds 	return;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 
6421da177e4SLinus Torvalds module_init(mon_init);
6431da177e4SLinus Torvalds module_exit(mon_exit);
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds module_param_string(mondcss, mon_dcss_name, 9, 0444);
6461da177e4SLinus Torvalds MODULE_PARM_DESC(mondcss, "Name of DCSS segment to be used for *MONITOR "
6471da177e4SLinus Torvalds 		 "service, max. 8 chars. Default is MONDCSS");
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds MODULE_AUTHOR("Gerald Schaefer <geraldsc@de.ibm.com>");
6501da177e4SLinus Torvalds MODULE_DESCRIPTION("Character device driver for reading z/VM "
6511da177e4SLinus Torvalds 		   "monitor service records.");
6521da177e4SLinus Torvalds MODULE_LICENSE("GPL");
653