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