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 28 static int mon_max_bufs = 255; 29 static int mon_buf_count; 30 31 struct mon_buf { 32 struct list_head list; 33 struct monwrite_hdr hdr; 34 int diag_done; 35 char *data; 36 }; 37 38 struct 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 50 static 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 70 static 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 88 static 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 133 static 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 173 static 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 186 static 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 204 static 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 252 out_error: 253 monpriv->data_to_read = 0; 254 monpriv->hdr_to_read = sizeof(struct monwrite_hdr); 255 return rc; 256 } 257 258 static const struct file_operations monwrite_fops = { 259 .owner = THIS_MODULE, 260 .open = &monwrite_open, 261 .release = &monwrite_close, 262 .write = &monwrite_write, 263 }; 264 265 static 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 275 static int __init mon_init(void) 276 { 277 if (MACHINE_IS_VM) 278 return misc_register(&mon_dev); 279 else 280 return -ENODEV; 281 } 282 283 static void __exit mon_exit(void) 284 { 285 WARN_ON(misc_deregister(&mon_dev) != 0); 286 } 287 288 module_init(mon_init); 289 module_exit(mon_exit); 290 291 module_param_named(max_bufs, mon_max_bufs, int, 0644); 292 MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers" 293 "that can be active at one time"); 294 295 MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>"); 296 MODULE_DESCRIPTION("Character device driver for writing z/VM " 297 "APPLDATA monitor records."); 298 MODULE_LICENSE("GPL"); 299