1/* 2 * drivers/s390/char/monwriter.c 3 * 4 * Character device driver for writing z/VM *MONITOR service records. 5 * 6 * Copyright (C) IBM Corp. 2006 7 * 8 * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com> 9 */ 10 11#include <linux/module.h> 12#include <linux/moduleparam.h> 13#include <linux/init.h> 14#include <linux/errno.h> 15#include <linux/types.h> 16#include <linux/kernel.h> 17#include <linux/miscdevice.h> 18#include <linux/ctype.h> 19#include <linux/poll.h> 20#include <asm/uaccess.h> 21#include <asm/ebcdic.h> 22#include <asm/io.h> 23#include <asm/appldata.h> 24#include <asm/monwriter.h> 25 26#define MONWRITE_MAX_DATALEN 4010 27 28static int mon_max_bufs = 255; 29static int mon_buf_count; 30 31struct mon_buf { 32 struct list_head list; 33 struct monwrite_hdr hdr; 34 int diag_done; 35 char *data; 36}; 37 38struct mon_private { 39 struct list_head list; 40 struct monwrite_hdr hdr; 41 size_t hdr_to_read; 42 size_t data_to_read; 43 struct mon_buf *current_buf; 44}; 45 46/* 47 * helper functions 48 */ 49 50static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn) 51{ 52 struct appldata_product_id id; 53 int rc; 54 55 strcpy(id.prod_nr, "LNXAPPL"); 56 id.prod_fn = myhdr->applid; 57 id.record_nr = myhdr->record_num; 58 id.version_nr = myhdr->version; 59 id.release_nr = myhdr->release; 60 id.mod_lvl = myhdr->mod_level; 61 rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen); 62 if (rc <= 0) 63 return rc; 64 if (rc == 5) 65 return -EPERM; 66 printk("DIAG X'DC' error with return code: %i\n", rc); 67 return -EINVAL; 68} 69 70static struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv, 71 struct monwrite_hdr *monhdr) 72{ 73 struct mon_buf *entry, *next; 74 75 list_for_each_entry_safe(entry, next, &monpriv->list, list) 76 if ((entry->hdr.mon_function == monhdr->mon_function || 77 monhdr->mon_function == MONWRITE_STOP_INTERVAL) && 78 entry->hdr.applid == monhdr->applid && 79 entry->hdr.record_num == monhdr->record_num && 80 entry->hdr.version == monhdr->version && 81 entry->hdr.release == monhdr->release && 82 entry->hdr.mod_level == monhdr->mod_level) 83 return entry; 84 85 return NULL; 86} 87 88static int monwrite_new_hdr(struct mon_private *monpriv) 89{ 90 struct monwrite_hdr *monhdr = &monpriv->hdr; 91 struct mon_buf *monbuf; 92 int rc; 93 94 if (monhdr->datalen > MONWRITE_MAX_DATALEN || 95 monhdr->mon_function > MONWRITE_START_CONFIG || 96 monhdr->hdrlen != sizeof(struct monwrite_hdr)) 97 return -EINVAL; 98 monbuf = NULL; 99 if (monhdr->mon_function != MONWRITE_GEN_EVENT) 100 monbuf = monwrite_find_hdr(monpriv, monhdr); 101 if (monbuf) { 102 if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) { 103 monhdr->datalen = monbuf->hdr.datalen; 104 rc = monwrite_diag(monhdr, monbuf->data, 105 APPLDATA_STOP_REC); 106 list_del(&monbuf->list); 107 mon_buf_count--; 108 kfree(monbuf->data); 109 kfree(monbuf); 110 monbuf = NULL; 111 } 112 } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) { 113 if (mon_buf_count >= mon_max_bufs) 114 return -ENOSPC; 115 monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL); 116 if (!monbuf) 117 return -ENOMEM; 118 monbuf->data = kzalloc(monhdr->datalen, 119 GFP_KERNEL | GFP_DMA); 120 if (!monbuf->data) { 121 kfree(monbuf); 122 return -ENOMEM; 123 } 124 monbuf->hdr = *monhdr; 125 list_add_tail(&monbuf->list, &monpriv->list); 126 if (monhdr->mon_function != MONWRITE_GEN_EVENT) 127 mon_buf_count++; 128 } 129 monpriv->current_buf = monbuf; 130 return 0; 131} 132 133static int monwrite_new_data(struct mon_private *monpriv) 134{ 135 struct monwrite_hdr *monhdr = &monpriv->hdr; 136 struct mon_buf *monbuf = monpriv->current_buf; 137 int rc = 0; 138 139 switch (monhdr->mon_function) { 140 case MONWRITE_START_INTERVAL: 141 if (!monbuf->diag_done) { 142 rc = monwrite_diag(monhdr, monbuf->data, 143 APPLDATA_START_INTERVAL_REC); 144 monbuf->diag_done = 1; 145 } 146 break; 147 case MONWRITE_START_CONFIG: 148 if (!monbuf->diag_done) { 149 rc = monwrite_diag(monhdr, monbuf->data, 150 APPLDATA_START_CONFIG_REC); 151 monbuf->diag_done = 1; 152 } 153 break; 154 case MONWRITE_GEN_EVENT: 155 rc = monwrite_diag(monhdr, monbuf->data, 156 APPLDATA_GEN_EVENT_REC); 157 list_del(&monpriv->current_buf->list); 158 kfree(monpriv->current_buf->data); 159 kfree(monpriv->current_buf); 160 monpriv->current_buf = NULL; 161 break; 162 default: 163 /* monhdr->mon_function is checked in monwrite_new_hdr */ 164 BUG(); 165 } 166 return rc; 167} 168 169/* 170 * file operations 171 */ 172 173static int monwrite_open(struct inode *inode, struct file *filp) 174{ 175 struct mon_private *monpriv; 176 177 monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); 178 if (!monpriv) 179 return -ENOMEM; 180 INIT_LIST_HEAD(&monpriv->list); 181 monpriv->hdr_to_read = sizeof(monpriv->hdr); 182 filp->private_data = monpriv; 183 return nonseekable_open(inode, filp); 184} 185 186static int monwrite_close(struct inode *inode, struct file *filp) 187{ 188 struct mon_private *monpriv = filp->private_data; 189 struct mon_buf *entry, *next; 190 191 list_for_each_entry_safe(entry, next, &monpriv->list, list) { 192 if (entry->hdr.mon_function != MONWRITE_GEN_EVENT) 193 monwrite_diag(&entry->hdr, entry->data, 194 APPLDATA_STOP_REC); 195 mon_buf_count--; 196 list_del(&entry->list); 197 kfree(entry->data); 198 kfree(entry); 199 } 200 kfree(monpriv); 201 return 0; 202} 203 204static ssize_t monwrite_write(struct file *filp, const char __user *data, 205 size_t count, loff_t *ppos) 206{ 207 struct mon_private *monpriv = filp->private_data; 208 size_t len, written; 209 void *to; 210 int rc; 211 212 for (written = 0; written < count; ) { 213 if (monpriv->hdr_to_read) { 214 len = min(count - written, monpriv->hdr_to_read); 215 to = (char *) &monpriv->hdr + 216 sizeof(monpriv->hdr) - monpriv->hdr_to_read; 217 if (copy_from_user(to, data + written, len)) { 218 rc = -EFAULT; 219 goto out_error; 220 } 221 monpriv->hdr_to_read -= len; 222 written += len; 223 if (monpriv->hdr_to_read > 0) 224 continue; 225 rc = monwrite_new_hdr(monpriv); 226 if (rc) 227 goto out_error; 228 monpriv->data_to_read = monpriv->current_buf ? 229 monpriv->current_buf->hdr.datalen : 0; 230 } 231 232 if (monpriv->data_to_read) { 233 len = min(count - written, monpriv->data_to_read); 234 to = monpriv->current_buf->data + 235 monpriv->hdr.datalen - monpriv->data_to_read; 236 if (copy_from_user(to, data + written, len)) { 237 rc = -EFAULT; 238 goto out_error; 239 } 240 monpriv->data_to_read -= len; 241 written += len; 242 if (monpriv->data_to_read > 0) 243 continue; 244 rc = monwrite_new_data(monpriv); 245 if (rc) 246 goto out_error; 247 } 248 monpriv->hdr_to_read = sizeof(monpriv->hdr); 249 } 250 return written; 251 252out_error: 253 monpriv->data_to_read = 0; 254 monpriv->hdr_to_read = sizeof(struct monwrite_hdr); 255 return rc; 256} 257 258static const struct file_operations monwrite_fops = { 259 .owner = THIS_MODULE, 260 .open = &monwrite_open, 261 .release = &monwrite_close, 262 .write = &monwrite_write, 263}; 264 265static struct miscdevice mon_dev = { 266 .name = "monwriter", 267 .fops = &monwrite_fops, 268 .minor = MISC_DYNAMIC_MINOR, 269}; 270 271/* 272 * module init/exit 273 */ 274 275static int __init mon_init(void) 276{ 277 if (MACHINE_IS_VM) 278 return misc_register(&mon_dev); 279 else 280 return -ENODEV; 281} 282 283static void __exit mon_exit(void) 284{ 285 WARN_ON(misc_deregister(&mon_dev) != 0); 286} 287 288module_init(mon_init); 289module_exit(mon_exit); 290 291module_param_named(max_bufs, mon_max_bufs, int, 0644); 292MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers" 293 "that can be active at one time"); 294 295MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>"); 296MODULE_DESCRIPTION("Character device driver for writing z/VM " 297 "APPLDATA monitor records."); 298MODULE_LICENSE("GPL"); 299