xref: /linux/drivers/s390/char/monwriter.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
16f05e69eSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
231b58088SMelissa Howland /*
331b58088SMelissa Howland  * Character device driver for writing z/VM *MONITOR service records.
431b58088SMelissa Howland  *
5fb78140cSGerald Schaefer  * Copyright IBM Corp. 2006, 2009
631b58088SMelissa Howland  *
731b58088SMelissa Howland  * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
831b58088SMelissa Howland  */
931b58088SMelissa Howland 
101519c0c6SMelissa Howland #define KMSG_COMPONENT "monwriter"
111519c0c6SMelissa Howland #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
121519c0c6SMelissa Howland 
1331b58088SMelissa Howland #include <linux/module.h>
1431b58088SMelissa Howland #include <linux/moduleparam.h>
1531b58088SMelissa Howland #include <linux/init.h>
1631b58088SMelissa Howland #include <linux/errno.h>
1731b58088SMelissa Howland #include <linux/types.h>
1831b58088SMelissa Howland #include <linux/kernel.h>
1931b58088SMelissa Howland #include <linux/miscdevice.h>
2031b58088SMelissa Howland #include <linux/ctype.h>
2131b58088SMelissa Howland #include <linux/poll.h>
22cbea66d9SMelissa Howland #include <linux/mutex.h>
23fb78140cSGerald Schaefer #include <linux/platform_device.h>
245a0e3ad6STejun Heo #include <linux/slab.h>
257c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2631b58088SMelissa Howland #include <asm/ebcdic.h>
2731b58088SMelissa Howland #include <asm/io.h>
2831b58088SMelissa Howland #include <asm/appldata.h>
2931b58088SMelissa Howland #include <asm/monwriter.h>
3031b58088SMelissa Howland 
31524a237eSMelissa Howland #define MONWRITE_MAX_DATALEN	4010
3231b58088SMelissa Howland 
3331b58088SMelissa Howland static int mon_max_bufs = 255;
342d103d5aSMelissa Howland static int mon_buf_count;
3531b58088SMelissa Howland 
3631b58088SMelissa Howland struct mon_buf {
3731b58088SMelissa Howland 	struct list_head list;
3831b58088SMelissa Howland 	struct monwrite_hdr hdr;
3931b58088SMelissa Howland 	int diag_done;
4031b58088SMelissa Howland 	char *data;
4131b58088SMelissa Howland };
4231b58088SMelissa Howland 
43fb78140cSGerald Schaefer static LIST_HEAD(mon_priv_list);
44fb78140cSGerald Schaefer 
4531b58088SMelissa Howland struct mon_private {
46fb78140cSGerald Schaefer 	struct list_head priv_list;
4731b58088SMelissa Howland 	struct list_head list;
4831b58088SMelissa Howland 	struct monwrite_hdr hdr;
4931b58088SMelissa Howland 	size_t hdr_to_read;
5031b58088SMelissa Howland 	size_t data_to_read;
5131b58088SMelissa Howland 	struct mon_buf *current_buf;
52cbea66d9SMelissa Howland 	struct mutex thread_mutex;
5331b58088SMelissa Howland };
5431b58088SMelissa Howland 
5531b58088SMelissa Howland /*
5631b58088SMelissa Howland  * helper functions
5731b58088SMelissa Howland  */
5831b58088SMelissa Howland 
5931b58088SMelissa Howland static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
6031b58088SMelissa Howland {
61*c0f07ff9SMartin Schwidefsky 	struct appldata_parameter_list *parm_list;
62*c0f07ff9SMartin Schwidefsky 	struct appldata_product_id *id;
6331b58088SMelissa Howland 	int rc;
6431b58088SMelissa Howland 
65*c0f07ff9SMartin Schwidefsky 	id = kmalloc(sizeof(*id), GFP_KERNEL);
66*c0f07ff9SMartin Schwidefsky 	parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
67*c0f07ff9SMartin Schwidefsky 	rc = -ENOMEM;
68*c0f07ff9SMartin Schwidefsky 	if (!id || !parm_list)
69*c0f07ff9SMartin Schwidefsky 		goto out;
70*c0f07ff9SMartin Schwidefsky 	memcpy(id->prod_nr, "LNXAPPL", 7);
71*c0f07ff9SMartin Schwidefsky 	id->prod_fn = myhdr->applid;
72*c0f07ff9SMartin Schwidefsky 	id->record_nr = myhdr->record_num;
73*c0f07ff9SMartin Schwidefsky 	id->version_nr = myhdr->version;
74*c0f07ff9SMartin Schwidefsky 	id->release_nr = myhdr->release;
75*c0f07ff9SMartin Schwidefsky 	id->mod_lvl = myhdr->mod_level;
76*c0f07ff9SMartin Schwidefsky 	rc = appldata_asm(parm_list, id, fcn,
77f689789aSMartin Schwidefsky 			  (void *) buffer, myhdr->datalen);
7831b58088SMelissa Howland 	if (rc <= 0)
79*c0f07ff9SMartin Schwidefsky 		goto out;
801519c0c6SMelissa Howland 	pr_err("Writing monitor data failed with rc=%i\n", rc);
81*c0f07ff9SMartin Schwidefsky 	rc = (rc == 5) ? -EPERM : -EINVAL;
82*c0f07ff9SMartin Schwidefsky out:
83*c0f07ff9SMartin Schwidefsky 	kfree(id);
84*c0f07ff9SMartin Schwidefsky 	kfree(parm_list);
85*c0f07ff9SMartin Schwidefsky 	return rc;
8631b58088SMelissa Howland }
8731b58088SMelissa Howland 
884d284cacSHeiko Carstens static struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
8931b58088SMelissa Howland 					 struct monwrite_hdr *monhdr)
9031b58088SMelissa Howland {
9131b58088SMelissa Howland 	struct mon_buf *entry, *next;
9231b58088SMelissa Howland 
9331b58088SMelissa Howland 	list_for_each_entry_safe(entry, next, &monpriv->list, list)
942c91971fSMelissa Howland 		if ((entry->hdr.mon_function == monhdr->mon_function ||
952c91971fSMelissa Howland 		     monhdr->mon_function == MONWRITE_STOP_INTERVAL) &&
962c91971fSMelissa Howland 		    entry->hdr.applid == monhdr->applid &&
9731b58088SMelissa Howland 		    entry->hdr.record_num == monhdr->record_num &&
9831b58088SMelissa Howland 		    entry->hdr.version == monhdr->version &&
9931b58088SMelissa Howland 		    entry->hdr.release == monhdr->release &&
10031b58088SMelissa Howland 		    entry->hdr.mod_level == monhdr->mod_level)
10131b58088SMelissa Howland 			return entry;
1022c91971fSMelissa Howland 
10331b58088SMelissa Howland 	return NULL;
10431b58088SMelissa Howland }
10531b58088SMelissa Howland 
10631b58088SMelissa Howland static int monwrite_new_hdr(struct mon_private *monpriv)
10731b58088SMelissa Howland {
10831b58088SMelissa Howland 	struct monwrite_hdr *monhdr = &monpriv->hdr;
10931b58088SMelissa Howland 	struct mon_buf *monbuf;
11037fa9975SHeiko Carstens 	int rc = 0;
11131b58088SMelissa Howland 
11231b58088SMelissa Howland 	if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
11331b58088SMelissa Howland 	    monhdr->mon_function > MONWRITE_START_CONFIG ||
11431b58088SMelissa Howland 	    monhdr->hdrlen != sizeof(struct monwrite_hdr))
11531b58088SMelissa Howland 		return -EINVAL;
1162c91971fSMelissa Howland 	monbuf = NULL;
1172c91971fSMelissa Howland 	if (monhdr->mon_function != MONWRITE_GEN_EVENT)
11831b58088SMelissa Howland 		monbuf = monwrite_find_hdr(monpriv, monhdr);
11931b58088SMelissa Howland 	if (monbuf) {
12031b58088SMelissa Howland 		if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
12131b58088SMelissa Howland 			monhdr->datalen = monbuf->hdr.datalen;
12231b58088SMelissa Howland 			rc = monwrite_diag(monhdr, monbuf->data,
12331b58088SMelissa Howland 					   APPLDATA_STOP_REC);
12431b58088SMelissa Howland 			list_del(&monbuf->list);
1252d103d5aSMelissa Howland 			mon_buf_count--;
12631b58088SMelissa Howland 			kfree(monbuf->data);
12731b58088SMelissa Howland 			kfree(monbuf);
12831b58088SMelissa Howland 			monbuf = NULL;
12931b58088SMelissa Howland 		}
1302c91971fSMelissa Howland 	} else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) {
1312d103d5aSMelissa Howland 		if (mon_buf_count >= mon_max_bufs)
13231b58088SMelissa Howland 			return -ENOSPC;
13331b58088SMelissa Howland 		monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
13431b58088SMelissa Howland 		if (!monbuf)
13531b58088SMelissa Howland 			return -ENOMEM;
136715d854bSMelissa Howland 		monbuf->data = kzalloc(monhdr->datalen,
13731b58088SMelissa Howland 				       GFP_KERNEL | GFP_DMA);
13831b58088SMelissa Howland 		if (!monbuf->data) {
13931b58088SMelissa Howland 			kfree(monbuf);
14031b58088SMelissa Howland 			return -ENOMEM;
14131b58088SMelissa Howland 		}
14231b58088SMelissa Howland 		monbuf->hdr = *monhdr;
14331b58088SMelissa Howland 		list_add_tail(&monbuf->list, &monpriv->list);
1442c91971fSMelissa Howland 		if (monhdr->mon_function != MONWRITE_GEN_EVENT)
1452d103d5aSMelissa Howland 			mon_buf_count++;
14631b58088SMelissa Howland 	}
14731b58088SMelissa Howland 	monpriv->current_buf = monbuf;
14837fa9975SHeiko Carstens 	return rc;
14931b58088SMelissa Howland }
15031b58088SMelissa Howland 
15131b58088SMelissa Howland static int monwrite_new_data(struct mon_private *monpriv)
15231b58088SMelissa Howland {
15331b58088SMelissa Howland 	struct monwrite_hdr *monhdr = &monpriv->hdr;
15431b58088SMelissa Howland 	struct mon_buf *monbuf = monpriv->current_buf;
15531b58088SMelissa Howland 	int rc = 0;
15631b58088SMelissa Howland 
15731b58088SMelissa Howland 	switch (monhdr->mon_function) {
15831b58088SMelissa Howland 	case MONWRITE_START_INTERVAL:
15931b58088SMelissa Howland 		if (!monbuf->diag_done) {
16031b58088SMelissa Howland 			rc = monwrite_diag(monhdr, monbuf->data,
16131b58088SMelissa Howland 					   APPLDATA_START_INTERVAL_REC);
16231b58088SMelissa Howland 			monbuf->diag_done = 1;
16331b58088SMelissa Howland 		}
16431b58088SMelissa Howland 		break;
16531b58088SMelissa Howland 	case MONWRITE_START_CONFIG:
16631b58088SMelissa Howland 		if (!monbuf->diag_done) {
16731b58088SMelissa Howland 			rc = monwrite_diag(monhdr, monbuf->data,
16831b58088SMelissa Howland 					   APPLDATA_START_CONFIG_REC);
16931b58088SMelissa Howland 			monbuf->diag_done = 1;
17031b58088SMelissa Howland 		}
17131b58088SMelissa Howland 		break;
17231b58088SMelissa Howland 	case MONWRITE_GEN_EVENT:
17331b58088SMelissa Howland 		rc = monwrite_diag(monhdr, monbuf->data,
17431b58088SMelissa Howland 				   APPLDATA_GEN_EVENT_REC);
17531b58088SMelissa Howland 		list_del(&monpriv->current_buf->list);
17631b58088SMelissa Howland 		kfree(monpriv->current_buf->data);
17731b58088SMelissa Howland 		kfree(monpriv->current_buf);
17831b58088SMelissa Howland 		monpriv->current_buf = NULL;
17931b58088SMelissa Howland 		break;
18031b58088SMelissa Howland 	default:
18131b58088SMelissa Howland 		/* monhdr->mon_function is checked in monwrite_new_hdr */
18231b58088SMelissa Howland 		BUG();
18331b58088SMelissa Howland 	}
18431b58088SMelissa Howland 	return rc;
18531b58088SMelissa Howland }
18631b58088SMelissa Howland 
18731b58088SMelissa Howland /*
18831b58088SMelissa Howland  * file operations
18931b58088SMelissa Howland  */
19031b58088SMelissa Howland 
19131b58088SMelissa Howland static int monwrite_open(struct inode *inode, struct file *filp)
19231b58088SMelissa Howland {
19331b58088SMelissa Howland 	struct mon_private *monpriv;
19431b58088SMelissa Howland 
19531b58088SMelissa Howland 	monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
19631b58088SMelissa Howland 	if (!monpriv)
19731b58088SMelissa Howland 		return -ENOMEM;
19831b58088SMelissa Howland 	INIT_LIST_HEAD(&monpriv->list);
19931b58088SMelissa Howland 	monpriv->hdr_to_read = sizeof(monpriv->hdr);
200cbea66d9SMelissa Howland 	mutex_init(&monpriv->thread_mutex);
20131b58088SMelissa Howland 	filp->private_data = monpriv;
202fb78140cSGerald Schaefer 	list_add_tail(&monpriv->priv_list, &mon_priv_list);
20331b58088SMelissa Howland 	return nonseekable_open(inode, filp);
20431b58088SMelissa Howland }
20531b58088SMelissa Howland 
20631b58088SMelissa Howland static int monwrite_close(struct inode *inode, struct file *filp)
20731b58088SMelissa Howland {
20831b58088SMelissa Howland 	struct mon_private *monpriv = filp->private_data;
20931b58088SMelissa Howland 	struct mon_buf *entry, *next;
21031b58088SMelissa Howland 
21131b58088SMelissa Howland 	list_for_each_entry_safe(entry, next, &monpriv->list, list) {
21231b58088SMelissa Howland 		if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
21331b58088SMelissa Howland 			monwrite_diag(&entry->hdr, entry->data,
21431b58088SMelissa Howland 				      APPLDATA_STOP_REC);
2152d103d5aSMelissa Howland 		mon_buf_count--;
21631b58088SMelissa Howland 		list_del(&entry->list);
21731b58088SMelissa Howland 		kfree(entry->data);
21831b58088SMelissa Howland 		kfree(entry);
21931b58088SMelissa Howland 	}
220fb78140cSGerald Schaefer 	list_del(&monpriv->priv_list);
22131b58088SMelissa Howland 	kfree(monpriv);
22231b58088SMelissa Howland 	return 0;
22331b58088SMelissa Howland }
22431b58088SMelissa Howland 
22531b58088SMelissa Howland static ssize_t monwrite_write(struct file *filp, const char __user *data,
22631b58088SMelissa Howland 			      size_t count, loff_t *ppos)
22731b58088SMelissa Howland {
22831b58088SMelissa Howland 	struct mon_private *monpriv = filp->private_data;
22931b58088SMelissa Howland 	size_t len, written;
23031b58088SMelissa Howland 	void *to;
23131b58088SMelissa Howland 	int rc;
23231b58088SMelissa Howland 
233cbea66d9SMelissa Howland 	mutex_lock(&monpriv->thread_mutex);
23431b58088SMelissa Howland 	for (written = 0; written < count; ) {
23531b58088SMelissa Howland 		if (monpriv->hdr_to_read) {
23631b58088SMelissa Howland 			len = min(count - written, monpriv->hdr_to_read);
23731b58088SMelissa Howland 			to = (char *) &monpriv->hdr +
23831b58088SMelissa Howland 				sizeof(monpriv->hdr) - monpriv->hdr_to_read;
23931b58088SMelissa Howland 			if (copy_from_user(to, data + written, len)) {
24031b58088SMelissa Howland 				rc = -EFAULT;
24131b58088SMelissa Howland 				goto out_error;
24231b58088SMelissa Howland 			}
24331b58088SMelissa Howland 			monpriv->hdr_to_read -= len;
24431b58088SMelissa Howland 			written += len;
24531b58088SMelissa Howland 			if (monpriv->hdr_to_read > 0)
24631b58088SMelissa Howland 				continue;
24731b58088SMelissa Howland 			rc = monwrite_new_hdr(monpriv);
24831b58088SMelissa Howland 			if (rc)
24931b58088SMelissa Howland 				goto out_error;
25031b58088SMelissa Howland 			monpriv->data_to_read = monpriv->current_buf ?
25131b58088SMelissa Howland 				monpriv->current_buf->hdr.datalen : 0;
25231b58088SMelissa Howland 		}
25331b58088SMelissa Howland 
25431b58088SMelissa Howland 		if (monpriv->data_to_read) {
25531b58088SMelissa Howland 			len = min(count - written, monpriv->data_to_read);
25631b58088SMelissa Howland 			to = monpriv->current_buf->data +
25731b58088SMelissa Howland 				monpriv->hdr.datalen - monpriv->data_to_read;
25831b58088SMelissa Howland 			if (copy_from_user(to, data + written, len)) {
25931b58088SMelissa Howland 				rc = -EFAULT;
26031b58088SMelissa Howland 				goto out_error;
26131b58088SMelissa Howland 			}
26231b58088SMelissa Howland 			monpriv->data_to_read -= len;
26331b58088SMelissa Howland 			written += len;
26431b58088SMelissa Howland 			if (monpriv->data_to_read > 0)
26531b58088SMelissa Howland 				continue;
26631b58088SMelissa Howland 			rc = monwrite_new_data(monpriv);
26731b58088SMelissa Howland 			if (rc)
26831b58088SMelissa Howland 				goto out_error;
26931b58088SMelissa Howland 		}
27031b58088SMelissa Howland 		monpriv->hdr_to_read = sizeof(monpriv->hdr);
27131b58088SMelissa Howland 	}
272cbea66d9SMelissa Howland 	mutex_unlock(&monpriv->thread_mutex);
27331b58088SMelissa Howland 	return written;
27431b58088SMelissa Howland 
27531b58088SMelissa Howland out_error:
27631b58088SMelissa Howland 	monpriv->data_to_read = 0;
27731b58088SMelissa Howland 	monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
278cbea66d9SMelissa Howland 	mutex_unlock(&monpriv->thread_mutex);
27931b58088SMelissa Howland 	return rc;
28031b58088SMelissa Howland }
28131b58088SMelissa Howland 
282d54b1fdbSArjan van de Ven static const struct file_operations monwrite_fops = {
28331b58088SMelissa Howland 	.owner	 = THIS_MODULE,
28431b58088SMelissa Howland 	.open	 = &monwrite_open,
28531b58088SMelissa Howland 	.release = &monwrite_close,
28631b58088SMelissa Howland 	.write	 = &monwrite_write,
2876038f373SArnd Bergmann 	.llseek  = noop_llseek,
28831b58088SMelissa Howland };
28931b58088SMelissa Howland 
29031b58088SMelissa Howland static struct miscdevice mon_dev = {
29131b58088SMelissa Howland 	.name	= "monwriter",
29231b58088SMelissa Howland 	.fops	= &monwrite_fops,
29331b58088SMelissa Howland 	.minor	= MISC_DYNAMIC_MINOR,
29431b58088SMelissa Howland };
29531b58088SMelissa Howland 
29631b58088SMelissa Howland /*
297fb78140cSGerald Schaefer  * suspend/resume
298fb78140cSGerald Schaefer  */
299fb78140cSGerald Schaefer 
300fb78140cSGerald Schaefer static int monwriter_freeze(struct device *dev)
301fb78140cSGerald Schaefer {
302fb78140cSGerald Schaefer 	struct mon_private *monpriv;
303fb78140cSGerald Schaefer 	struct mon_buf *monbuf;
304fb78140cSGerald Schaefer 
305fb78140cSGerald Schaefer 	list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
306fb78140cSGerald Schaefer 		list_for_each_entry(monbuf, &monpriv->list, list) {
307fb78140cSGerald Schaefer 			if (monbuf->hdr.mon_function != MONWRITE_GEN_EVENT)
308fb78140cSGerald Schaefer 				monwrite_diag(&monbuf->hdr, monbuf->data,
309fb78140cSGerald Schaefer 					      APPLDATA_STOP_REC);
310fb78140cSGerald Schaefer 		}
311fb78140cSGerald Schaefer 	}
312fb78140cSGerald Schaefer 	return 0;
313fb78140cSGerald Schaefer }
314fb78140cSGerald Schaefer 
315fb78140cSGerald Schaefer static int monwriter_restore(struct device *dev)
316fb78140cSGerald Schaefer {
317fb78140cSGerald Schaefer 	struct mon_private *monpriv;
318fb78140cSGerald Schaefer 	struct mon_buf *monbuf;
319fb78140cSGerald Schaefer 
320fb78140cSGerald Schaefer 	list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
321fb78140cSGerald Schaefer 		list_for_each_entry(monbuf, &monpriv->list, list) {
322fb78140cSGerald Schaefer 			if (monbuf->hdr.mon_function == MONWRITE_START_INTERVAL)
323fb78140cSGerald Schaefer 				monwrite_diag(&monbuf->hdr, monbuf->data,
324fb78140cSGerald Schaefer 					      APPLDATA_START_INTERVAL_REC);
325fb78140cSGerald Schaefer 			if (monbuf->hdr.mon_function == MONWRITE_START_CONFIG)
326fb78140cSGerald Schaefer 				monwrite_diag(&monbuf->hdr, monbuf->data,
327fb78140cSGerald Schaefer 					      APPLDATA_START_CONFIG_REC);
328fb78140cSGerald Schaefer 		}
329fb78140cSGerald Schaefer 	}
330fb78140cSGerald Schaefer 	return 0;
331fb78140cSGerald Schaefer }
332fb78140cSGerald Schaefer 
333fb78140cSGerald Schaefer static int monwriter_thaw(struct device *dev)
334fb78140cSGerald Schaefer {
335fb78140cSGerald Schaefer 	return monwriter_restore(dev);
336fb78140cSGerald Schaefer }
337fb78140cSGerald Schaefer 
33847145210SAlexey Dobriyan static const struct dev_pm_ops monwriter_pm_ops = {
339fb78140cSGerald Schaefer 	.freeze		= monwriter_freeze,
340fb78140cSGerald Schaefer 	.thaw		= monwriter_thaw,
341fb78140cSGerald Schaefer 	.restore	= monwriter_restore,
342fb78140cSGerald Schaefer };
343fb78140cSGerald Schaefer 
344fb78140cSGerald Schaefer static struct platform_driver monwriter_pdrv = {
345fb78140cSGerald Schaefer 	.driver = {
346fb78140cSGerald Schaefer 		.name	= "monwriter",
347fb78140cSGerald Schaefer 		.pm	= &monwriter_pm_ops,
348fb78140cSGerald Schaefer 	},
349fb78140cSGerald Schaefer };
350fb78140cSGerald Schaefer 
351fb78140cSGerald Schaefer static struct platform_device *monwriter_pdev;
352fb78140cSGerald Schaefer 
353fb78140cSGerald Schaefer /*
35431b58088SMelissa Howland  * module init/exit
35531b58088SMelissa Howland  */
35631b58088SMelissa Howland 
35731b58088SMelissa Howland static int __init mon_init(void)
35831b58088SMelissa Howland {
359fb78140cSGerald Schaefer 	int rc;
360fb78140cSGerald Schaefer 
361fb78140cSGerald Schaefer 	if (!MACHINE_IS_VM)
36231b58088SMelissa Howland 		return -ENODEV;
363fb78140cSGerald Schaefer 
364fb78140cSGerald Schaefer 	rc = platform_driver_register(&monwriter_pdrv);
365fb78140cSGerald Schaefer 	if (rc)
366fb78140cSGerald Schaefer 		return rc;
367fb78140cSGerald Schaefer 
368fb78140cSGerald Schaefer 	monwriter_pdev = platform_device_register_simple("monwriter", -1, NULL,
369fb78140cSGerald Schaefer 							0);
370fb78140cSGerald Schaefer 	if (IS_ERR(monwriter_pdev)) {
371fb78140cSGerald Schaefer 		rc = PTR_ERR(monwriter_pdev);
372fb78140cSGerald Schaefer 		goto out_driver;
373fb78140cSGerald Schaefer 	}
374fb78140cSGerald Schaefer 
375801f97b7SGerald Schaefer 	/*
376801f97b7SGerald Schaefer 	 * misc_register() has to be the last action in module_init(), because
377801f97b7SGerald Schaefer 	 * file operations will be available right after this.
378801f97b7SGerald Schaefer 	 */
379fb78140cSGerald Schaefer 	rc = misc_register(&mon_dev);
380fb78140cSGerald Schaefer 	if (rc)
381fb78140cSGerald Schaefer 		goto out_device;
382fb78140cSGerald Schaefer 	return 0;
383fb78140cSGerald Schaefer 
384fb78140cSGerald Schaefer out_device:
385fb78140cSGerald Schaefer 	platform_device_unregister(monwriter_pdev);
386fb78140cSGerald Schaefer out_driver:
387fb78140cSGerald Schaefer 	platform_driver_unregister(&monwriter_pdrv);
388fb78140cSGerald Schaefer 	return rc;
38931b58088SMelissa Howland }
39031b58088SMelissa Howland 
39131b58088SMelissa Howland static void __exit mon_exit(void)
39231b58088SMelissa Howland {
393547415d5SAkinobu Mita 	misc_deregister(&mon_dev);
394fb78140cSGerald Schaefer 	platform_device_unregister(monwriter_pdev);
395fb78140cSGerald Schaefer 	platform_driver_unregister(&monwriter_pdrv);
39631b58088SMelissa Howland }
39731b58088SMelissa Howland 
39831b58088SMelissa Howland module_init(mon_init);
39931b58088SMelissa Howland module_exit(mon_exit);
40031b58088SMelissa Howland 
40131b58088SMelissa Howland module_param_named(max_bufs, mon_max_bufs, int, 0644);
40231b58088SMelissa Howland MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers "
40331b58088SMelissa Howland 		 "that can be active at one time");
40431b58088SMelissa Howland 
40531b58088SMelissa Howland MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
40631b58088SMelissa Howland MODULE_DESCRIPTION("Character device driver for writing z/VM "
40731b58088SMelissa Howland 		   "APPLDATA monitor records.");
40831b58088SMelissa Howland MODULE_LICENSE("GPL");
409