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>
235a0e3ad6STejun Heo #include <linux/slab.h>
247c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
25*b378a982SHeiko Carstens #include <linux/io.h>
2631b58088SMelissa Howland #include <asm/ebcdic.h>
2731b58088SMelissa Howland #include <asm/appldata.h>
2831b58088SMelissa Howland #include <asm/monwriter.h>
2931b58088SMelissa Howland
30524a237eSMelissa Howland #define MONWRITE_MAX_DATALEN 4010
3131b58088SMelissa Howland
3231b58088SMelissa Howland static int mon_max_bufs = 255;
332d103d5aSMelissa Howland static int mon_buf_count;
3431b58088SMelissa Howland
3531b58088SMelissa Howland struct mon_buf {
3631b58088SMelissa Howland struct list_head list;
3731b58088SMelissa Howland struct monwrite_hdr hdr;
3831b58088SMelissa Howland int diag_done;
3931b58088SMelissa Howland char *data;
4031b58088SMelissa Howland };
4131b58088SMelissa Howland
4231b58088SMelissa Howland struct mon_private {
4331b58088SMelissa Howland struct list_head list;
4431b58088SMelissa Howland struct monwrite_hdr hdr;
4531b58088SMelissa Howland size_t hdr_to_read;
4631b58088SMelissa Howland size_t data_to_read;
4731b58088SMelissa Howland struct mon_buf *current_buf;
48cbea66d9SMelissa Howland struct mutex thread_mutex;
4931b58088SMelissa Howland };
5031b58088SMelissa Howland
5131b58088SMelissa Howland /*
5231b58088SMelissa Howland * helper functions
5331b58088SMelissa Howland */
5431b58088SMelissa Howland
monwrite_diag(struct monwrite_hdr * myhdr,char * buffer,int fcn)5531b58088SMelissa Howland static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
5631b58088SMelissa Howland {
57c0f07ff9SMartin Schwidefsky struct appldata_parameter_list *parm_list;
58c0f07ff9SMartin Schwidefsky struct appldata_product_id *id;
5931b58088SMelissa Howland int rc;
6031b58088SMelissa Howland
61c0f07ff9SMartin Schwidefsky id = kmalloc(sizeof(*id), GFP_KERNEL);
62c0f07ff9SMartin Schwidefsky parm_list = kmalloc(sizeof(*parm_list), GFP_KERNEL);
63c0f07ff9SMartin Schwidefsky rc = -ENOMEM;
64c0f07ff9SMartin Schwidefsky if (!id || !parm_list)
65c0f07ff9SMartin Schwidefsky goto out;
66c0f07ff9SMartin Schwidefsky memcpy(id->prod_nr, "LNXAPPL", 7);
67c0f07ff9SMartin Schwidefsky id->prod_fn = myhdr->applid;
68c0f07ff9SMartin Schwidefsky id->record_nr = myhdr->record_num;
69c0f07ff9SMartin Schwidefsky id->version_nr = myhdr->version;
70c0f07ff9SMartin Schwidefsky id->release_nr = myhdr->release;
71c0f07ff9SMartin Schwidefsky id->mod_lvl = myhdr->mod_level;
72c0f07ff9SMartin Schwidefsky rc = appldata_asm(parm_list, id, fcn,
73f689789aSMartin Schwidefsky (void *) buffer, myhdr->datalen);
7431b58088SMelissa Howland if (rc <= 0)
75c0f07ff9SMartin Schwidefsky goto out;
761519c0c6SMelissa Howland pr_err("Writing monitor data failed with rc=%i\n", rc);
77c0f07ff9SMartin Schwidefsky rc = (rc == 5) ? -EPERM : -EINVAL;
78c0f07ff9SMartin Schwidefsky out:
79c0f07ff9SMartin Schwidefsky kfree(id);
80c0f07ff9SMartin Schwidefsky kfree(parm_list);
81c0f07ff9SMartin Schwidefsky return rc;
8231b58088SMelissa Howland }
8331b58088SMelissa Howland
monwrite_find_hdr(struct mon_private * monpriv,struct monwrite_hdr * monhdr)844d284cacSHeiko Carstens static struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
8531b58088SMelissa Howland struct monwrite_hdr *monhdr)
8631b58088SMelissa Howland {
8731b58088SMelissa Howland struct mon_buf *entry, *next;
8831b58088SMelissa Howland
8931b58088SMelissa Howland list_for_each_entry_safe(entry, next, &monpriv->list, list)
902c91971fSMelissa Howland if ((entry->hdr.mon_function == monhdr->mon_function ||
912c91971fSMelissa Howland monhdr->mon_function == MONWRITE_STOP_INTERVAL) &&
922c91971fSMelissa Howland entry->hdr.applid == monhdr->applid &&
9331b58088SMelissa Howland entry->hdr.record_num == monhdr->record_num &&
9431b58088SMelissa Howland entry->hdr.version == monhdr->version &&
9531b58088SMelissa Howland entry->hdr.release == monhdr->release &&
9631b58088SMelissa Howland entry->hdr.mod_level == monhdr->mod_level)
9731b58088SMelissa Howland return entry;
982c91971fSMelissa Howland
9931b58088SMelissa Howland return NULL;
10031b58088SMelissa Howland }
10131b58088SMelissa Howland
monwrite_new_hdr(struct mon_private * monpriv)10231b58088SMelissa Howland static int monwrite_new_hdr(struct mon_private *monpriv)
10331b58088SMelissa Howland {
10431b58088SMelissa Howland struct monwrite_hdr *monhdr = &monpriv->hdr;
10531b58088SMelissa Howland struct mon_buf *monbuf;
10637fa9975SHeiko Carstens int rc = 0;
10731b58088SMelissa Howland
10831b58088SMelissa Howland if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
10931b58088SMelissa Howland monhdr->mon_function > MONWRITE_START_CONFIG ||
11031b58088SMelissa Howland monhdr->hdrlen != sizeof(struct monwrite_hdr))
11131b58088SMelissa Howland return -EINVAL;
1122c91971fSMelissa Howland monbuf = NULL;
1132c91971fSMelissa Howland if (monhdr->mon_function != MONWRITE_GEN_EVENT)
11431b58088SMelissa Howland monbuf = monwrite_find_hdr(monpriv, monhdr);
11531b58088SMelissa Howland if (monbuf) {
11631b58088SMelissa Howland if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
11731b58088SMelissa Howland monhdr->datalen = monbuf->hdr.datalen;
11831b58088SMelissa Howland rc = monwrite_diag(monhdr, monbuf->data,
11931b58088SMelissa Howland APPLDATA_STOP_REC);
12031b58088SMelissa Howland list_del(&monbuf->list);
1212d103d5aSMelissa Howland mon_buf_count--;
12231b58088SMelissa Howland kfree(monbuf->data);
12331b58088SMelissa Howland kfree(monbuf);
12431b58088SMelissa Howland monbuf = NULL;
12531b58088SMelissa Howland }
1262c91971fSMelissa Howland } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) {
1272d103d5aSMelissa Howland if (mon_buf_count >= mon_max_bufs)
12831b58088SMelissa Howland return -ENOSPC;
12931b58088SMelissa Howland monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
13031b58088SMelissa Howland if (!monbuf)
13131b58088SMelissa Howland return -ENOMEM;
132715d854bSMelissa Howland monbuf->data = kzalloc(monhdr->datalen,
13331b58088SMelissa Howland GFP_KERNEL | GFP_DMA);
13431b58088SMelissa Howland if (!monbuf->data) {
13531b58088SMelissa Howland kfree(monbuf);
13631b58088SMelissa Howland return -ENOMEM;
13731b58088SMelissa Howland }
13831b58088SMelissa Howland monbuf->hdr = *monhdr;
13931b58088SMelissa Howland list_add_tail(&monbuf->list, &monpriv->list);
1402c91971fSMelissa Howland if (monhdr->mon_function != MONWRITE_GEN_EVENT)
1412d103d5aSMelissa Howland mon_buf_count++;
14231b58088SMelissa Howland }
14331b58088SMelissa Howland monpriv->current_buf = monbuf;
14437fa9975SHeiko Carstens return rc;
14531b58088SMelissa Howland }
14631b58088SMelissa Howland
monwrite_new_data(struct mon_private * monpriv)14731b58088SMelissa Howland static int monwrite_new_data(struct mon_private *monpriv)
14831b58088SMelissa Howland {
14931b58088SMelissa Howland struct monwrite_hdr *monhdr = &monpriv->hdr;
15031b58088SMelissa Howland struct mon_buf *monbuf = monpriv->current_buf;
15131b58088SMelissa Howland int rc = 0;
15231b58088SMelissa Howland
15331b58088SMelissa Howland switch (monhdr->mon_function) {
15431b58088SMelissa Howland case MONWRITE_START_INTERVAL:
15531b58088SMelissa Howland if (!monbuf->diag_done) {
15631b58088SMelissa Howland rc = monwrite_diag(monhdr, monbuf->data,
15731b58088SMelissa Howland APPLDATA_START_INTERVAL_REC);
15831b58088SMelissa Howland monbuf->diag_done = 1;
15931b58088SMelissa Howland }
16031b58088SMelissa Howland break;
16131b58088SMelissa Howland case MONWRITE_START_CONFIG:
16231b58088SMelissa Howland if (!monbuf->diag_done) {
16331b58088SMelissa Howland rc = monwrite_diag(monhdr, monbuf->data,
16431b58088SMelissa Howland APPLDATA_START_CONFIG_REC);
16531b58088SMelissa Howland monbuf->diag_done = 1;
16631b58088SMelissa Howland }
16731b58088SMelissa Howland break;
16831b58088SMelissa Howland case MONWRITE_GEN_EVENT:
16931b58088SMelissa Howland rc = monwrite_diag(monhdr, monbuf->data,
17031b58088SMelissa Howland APPLDATA_GEN_EVENT_REC);
17131b58088SMelissa Howland list_del(&monpriv->current_buf->list);
17231b58088SMelissa Howland kfree(monpriv->current_buf->data);
17331b58088SMelissa Howland kfree(monpriv->current_buf);
17431b58088SMelissa Howland monpriv->current_buf = NULL;
17531b58088SMelissa Howland break;
17631b58088SMelissa Howland default:
17731b58088SMelissa Howland /* monhdr->mon_function is checked in monwrite_new_hdr */
17831b58088SMelissa Howland BUG();
17931b58088SMelissa Howland }
18031b58088SMelissa Howland return rc;
18131b58088SMelissa Howland }
18231b58088SMelissa Howland
18331b58088SMelissa Howland /*
18431b58088SMelissa Howland * file operations
18531b58088SMelissa Howland */
18631b58088SMelissa Howland
monwrite_open(struct inode * inode,struct file * filp)18731b58088SMelissa Howland static int monwrite_open(struct inode *inode, struct file *filp)
18831b58088SMelissa Howland {
18931b58088SMelissa Howland struct mon_private *monpriv;
19031b58088SMelissa Howland
19131b58088SMelissa Howland monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
19231b58088SMelissa Howland if (!monpriv)
19331b58088SMelissa Howland return -ENOMEM;
19431b58088SMelissa Howland INIT_LIST_HEAD(&monpriv->list);
19531b58088SMelissa Howland monpriv->hdr_to_read = sizeof(monpriv->hdr);
196cbea66d9SMelissa Howland mutex_init(&monpriv->thread_mutex);
19731b58088SMelissa Howland filp->private_data = monpriv;
19831b58088SMelissa Howland return nonseekable_open(inode, filp);
19931b58088SMelissa Howland }
20031b58088SMelissa Howland
monwrite_close(struct inode * inode,struct file * filp)20131b58088SMelissa Howland static int monwrite_close(struct inode *inode, struct file *filp)
20231b58088SMelissa Howland {
20331b58088SMelissa Howland struct mon_private *monpriv = filp->private_data;
20431b58088SMelissa Howland struct mon_buf *entry, *next;
20531b58088SMelissa Howland
20631b58088SMelissa Howland list_for_each_entry_safe(entry, next, &monpriv->list, list) {
20731b58088SMelissa Howland if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
20831b58088SMelissa Howland monwrite_diag(&entry->hdr, entry->data,
20931b58088SMelissa Howland APPLDATA_STOP_REC);
2102d103d5aSMelissa Howland mon_buf_count--;
21131b58088SMelissa Howland list_del(&entry->list);
21231b58088SMelissa Howland kfree(entry->data);
21331b58088SMelissa Howland kfree(entry);
21431b58088SMelissa Howland }
21531b58088SMelissa Howland kfree(monpriv);
21631b58088SMelissa Howland return 0;
21731b58088SMelissa Howland }
21831b58088SMelissa Howland
monwrite_write(struct file * filp,const char __user * data,size_t count,loff_t * ppos)21931b58088SMelissa Howland static ssize_t monwrite_write(struct file *filp, const char __user *data,
22031b58088SMelissa Howland size_t count, loff_t *ppos)
22131b58088SMelissa Howland {
22231b58088SMelissa Howland struct mon_private *monpriv = filp->private_data;
22331b58088SMelissa Howland size_t len, written;
22431b58088SMelissa Howland void *to;
22531b58088SMelissa Howland int rc;
22631b58088SMelissa Howland
227cbea66d9SMelissa Howland mutex_lock(&monpriv->thread_mutex);
22831b58088SMelissa Howland for (written = 0; written < count; ) {
22931b58088SMelissa Howland if (monpriv->hdr_to_read) {
23031b58088SMelissa Howland len = min(count - written, monpriv->hdr_to_read);
23131b58088SMelissa Howland to = (char *) &monpriv->hdr +
23231b58088SMelissa Howland sizeof(monpriv->hdr) - monpriv->hdr_to_read;
23331b58088SMelissa Howland if (copy_from_user(to, data + written, len)) {
23431b58088SMelissa Howland rc = -EFAULT;
23531b58088SMelissa Howland goto out_error;
23631b58088SMelissa Howland }
23731b58088SMelissa Howland monpriv->hdr_to_read -= len;
23831b58088SMelissa Howland written += len;
23931b58088SMelissa Howland if (monpriv->hdr_to_read > 0)
24031b58088SMelissa Howland continue;
24131b58088SMelissa Howland rc = monwrite_new_hdr(monpriv);
24231b58088SMelissa Howland if (rc)
24331b58088SMelissa Howland goto out_error;
24431b58088SMelissa Howland monpriv->data_to_read = monpriv->current_buf ?
24531b58088SMelissa Howland monpriv->current_buf->hdr.datalen : 0;
24631b58088SMelissa Howland }
24731b58088SMelissa Howland
24831b58088SMelissa Howland if (monpriv->data_to_read) {
24931b58088SMelissa Howland len = min(count - written, monpriv->data_to_read);
25031b58088SMelissa Howland to = monpriv->current_buf->data +
25131b58088SMelissa Howland monpriv->hdr.datalen - monpriv->data_to_read;
25231b58088SMelissa Howland if (copy_from_user(to, data + written, len)) {
25331b58088SMelissa Howland rc = -EFAULT;
25431b58088SMelissa Howland goto out_error;
25531b58088SMelissa Howland }
25631b58088SMelissa Howland monpriv->data_to_read -= len;
25731b58088SMelissa Howland written += len;
25831b58088SMelissa Howland if (monpriv->data_to_read > 0)
25931b58088SMelissa Howland continue;
26031b58088SMelissa Howland rc = monwrite_new_data(monpriv);
26131b58088SMelissa Howland if (rc)
26231b58088SMelissa Howland goto out_error;
26331b58088SMelissa Howland }
26431b58088SMelissa Howland monpriv->hdr_to_read = sizeof(monpriv->hdr);
26531b58088SMelissa Howland }
266cbea66d9SMelissa Howland mutex_unlock(&monpriv->thread_mutex);
26731b58088SMelissa Howland return written;
26831b58088SMelissa Howland
26931b58088SMelissa Howland out_error:
27031b58088SMelissa Howland monpriv->data_to_read = 0;
27131b58088SMelissa Howland monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
272cbea66d9SMelissa Howland mutex_unlock(&monpriv->thread_mutex);
27331b58088SMelissa Howland return rc;
27431b58088SMelissa Howland }
27531b58088SMelissa Howland
276d54b1fdbSArjan van de Ven static const struct file_operations monwrite_fops = {
27731b58088SMelissa Howland .owner = THIS_MODULE,
27831b58088SMelissa Howland .open = &monwrite_open,
27931b58088SMelissa Howland .release = &monwrite_close,
28031b58088SMelissa Howland .write = &monwrite_write,
2816038f373SArnd Bergmann .llseek = noop_llseek,
28231b58088SMelissa Howland };
28331b58088SMelissa Howland
28431b58088SMelissa Howland static struct miscdevice mon_dev = {
28531b58088SMelissa Howland .name = "monwriter",
28631b58088SMelissa Howland .fops = &monwrite_fops,
28731b58088SMelissa Howland .minor = MISC_DYNAMIC_MINOR,
28831b58088SMelissa Howland };
28931b58088SMelissa Howland
29031b58088SMelissa Howland /*
29131b58088SMelissa Howland * module init/exit
29231b58088SMelissa Howland */
29331b58088SMelissa Howland
mon_init(void)29431b58088SMelissa Howland static int __init mon_init(void)
29531b58088SMelissa Howland {
296fb78140cSGerald Schaefer if (!MACHINE_IS_VM)
29731b58088SMelissa Howland return -ENODEV;
298801f97b7SGerald Schaefer /*
299801f97b7SGerald Schaefer * misc_register() has to be the last action in module_init(), because
300801f97b7SGerald Schaefer * file operations will be available right after this.
301801f97b7SGerald Schaefer */
30221adcf11SPeter Oberparleiter return misc_register(&mon_dev);
30331b58088SMelissa Howland }
30431b58088SMelissa Howland
mon_exit(void)30531b58088SMelissa Howland static void __exit mon_exit(void)
30631b58088SMelissa Howland {
307547415d5SAkinobu Mita misc_deregister(&mon_dev);
30831b58088SMelissa Howland }
30931b58088SMelissa Howland
31031b58088SMelissa Howland module_init(mon_init);
31131b58088SMelissa Howland module_exit(mon_exit);
31231b58088SMelissa Howland
31331b58088SMelissa Howland module_param_named(max_bufs, mon_max_bufs, int, 0644);
31431b58088SMelissa Howland MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers "
31531b58088SMelissa Howland "that can be active at one time");
31631b58088SMelissa Howland
31731b58088SMelissa Howland MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
31831b58088SMelissa Howland MODULE_DESCRIPTION("Character device driver for writing z/VM "
31931b58088SMelissa Howland "APPLDATA monitor records.");
32031b58088SMelissa Howland MODULE_LICENSE("GPL");
321