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