linux/drivers/s390/char/monwriter.c
<<
>>
Prefs
   1/*
   2 * Character device driver for writing z/VM *MONITOR service records.
   3 *
   4 * Copyright IBM Corp. 2006, 2009
   5 *
   6 * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
   7 */
   8
   9#define KMSG_COMPONENT "monwriter"
  10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  11
  12#include <linux/module.h>
  13#include <linux/moduleparam.h>
  14#include <linux/init.h>
  15#include <linux/errno.h>
  16#include <linux/types.h>
  17#include <linux/kernel.h>
  18#include <linux/miscdevice.h>
  19#include <linux/ctype.h>
  20#include <linux/poll.h>
  21#include <linux/mutex.h>
  22#include <linux/platform_device.h>
  23#include <linux/slab.h>
  24#include <asm/uaccess.h>
  25#include <asm/ebcdic.h>
  26#include <asm/io.h>
  27#include <asm/appldata.h>
  28#include <asm/monwriter.h>
  29
  30#define MONWRITE_MAX_DATALEN    4010
  31
  32static int mon_max_bufs = 255;
  33static int mon_buf_count;
  34
  35struct mon_buf {
  36        struct list_head list;
  37        struct monwrite_hdr hdr;
  38        int diag_done;
  39        char *data;
  40};
  41
  42static LIST_HEAD(mon_priv_list);
  43
  44struct mon_private {
  45        struct list_head priv_list;
  46        struct list_head list;
  47        struct monwrite_hdr hdr;
  48        size_t hdr_to_read;
  49        size_t data_to_read;
  50        struct mon_buf *current_buf;
  51        struct mutex thread_mutex;
  52};
  53
  54/*
  55 * helper functions
  56 */
  57
  58static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
  59{
  60        struct appldata_product_id id;
  61        int rc;
  62
  63        strncpy(id.prod_nr, "LNXAPPL", 7);
  64        id.prod_fn = myhdr->applid;
  65        id.record_nr = myhdr->record_num;
  66        id.version_nr = myhdr->version;
  67        id.release_nr = myhdr->release;
  68        id.mod_lvl = myhdr->mod_level;
  69        rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
  70        if (rc <= 0)
  71                return rc;
  72        pr_err("Writing monitor data failed with rc=%i\n", rc);
  73        if (rc == 5)
  74                return -EPERM;
  75        return -EINVAL;
  76}
  77
  78static struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
  79                                         struct monwrite_hdr *monhdr)
  80{
  81        struct mon_buf *entry, *next;
  82
  83        list_for_each_entry_safe(entry, next, &monpriv->list, list)
  84                if ((entry->hdr.mon_function == monhdr->mon_function ||
  85                     monhdr->mon_function == MONWRITE_STOP_INTERVAL) &&
  86                    entry->hdr.applid == monhdr->applid &&
  87                    entry->hdr.record_num == monhdr->record_num &&
  88                    entry->hdr.version == monhdr->version &&
  89                    entry->hdr.release == monhdr->release &&
  90                    entry->hdr.mod_level == monhdr->mod_level)
  91                        return entry;
  92
  93        return NULL;
  94}
  95
  96static int monwrite_new_hdr(struct mon_private *monpriv)
  97{
  98        struct monwrite_hdr *monhdr = &monpriv->hdr;
  99        struct mon_buf *monbuf;
 100        int rc = 0;
 101
 102        if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
 103            monhdr->mon_function > MONWRITE_START_CONFIG ||
 104            monhdr->hdrlen != sizeof(struct monwrite_hdr))
 105                return -EINVAL;
 106        monbuf = NULL;
 107        if (monhdr->mon_function != MONWRITE_GEN_EVENT)
 108                monbuf = monwrite_find_hdr(monpriv, monhdr);
 109        if (monbuf) {
 110                if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
 111                        monhdr->datalen = monbuf->hdr.datalen;
 112                        rc = monwrite_diag(monhdr, monbuf->data,
 113                                           APPLDATA_STOP_REC);
 114                        list_del(&monbuf->list);
 115                        mon_buf_count--;
 116                        kfree(monbuf->data);
 117                        kfree(monbuf);
 118                        monbuf = NULL;
 119                }
 120        } else if (monhdr->mon_function != MONWRITE_STOP_INTERVAL) {
 121                if (mon_buf_count >= mon_max_bufs)
 122                        return -ENOSPC;
 123                monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
 124                if (!monbuf)
 125                        return -ENOMEM;
 126                monbuf->data = kzalloc(monhdr->datalen,
 127                                       GFP_KERNEL | GFP_DMA);
 128                if (!monbuf->data) {
 129                        kfree(monbuf);
 130                        return -ENOMEM;
 131                }
 132                monbuf->hdr = *monhdr;
 133                list_add_tail(&monbuf->list, &monpriv->list);
 134                if (monhdr->mon_function != MONWRITE_GEN_EVENT)
 135                        mon_buf_count++;
 136        }
 137        monpriv->current_buf = monbuf;
 138        return rc;
 139}
 140
 141static int monwrite_new_data(struct mon_private *monpriv)
 142{
 143        struct monwrite_hdr *monhdr = &monpriv->hdr;
 144        struct mon_buf *monbuf = monpriv->current_buf;
 145        int rc = 0;
 146
 147        switch (monhdr->mon_function) {
 148        case MONWRITE_START_INTERVAL:
 149                if (!monbuf->diag_done) {
 150                        rc = monwrite_diag(monhdr, monbuf->data,
 151                                           APPLDATA_START_INTERVAL_REC);
 152                        monbuf->diag_done = 1;
 153                }
 154                break;
 155        case MONWRITE_START_CONFIG:
 156                if (!monbuf->diag_done) {
 157                        rc = monwrite_diag(monhdr, monbuf->data,
 158                                           APPLDATA_START_CONFIG_REC);
 159                        monbuf->diag_done = 1;
 160                }
 161                break;
 162        case MONWRITE_GEN_EVENT:
 163                rc = monwrite_diag(monhdr, monbuf->data,
 164                                   APPLDATA_GEN_EVENT_REC);
 165                list_del(&monpriv->current_buf->list);
 166                kfree(monpriv->current_buf->data);
 167                kfree(monpriv->current_buf);
 168                monpriv->current_buf = NULL;
 169                break;
 170        default:
 171                /* monhdr->mon_function is checked in monwrite_new_hdr */
 172                BUG();
 173        }
 174        return rc;
 175}
 176
 177/*
 178 * file operations
 179 */
 180
 181static int monwrite_open(struct inode *inode, struct file *filp)
 182{
 183        struct mon_private *monpriv;
 184
 185        monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
 186        if (!monpriv)
 187                return -ENOMEM;
 188        INIT_LIST_HEAD(&monpriv->list);
 189        monpriv->hdr_to_read = sizeof(monpriv->hdr);
 190        mutex_init(&monpriv->thread_mutex);
 191        filp->private_data = monpriv;
 192        list_add_tail(&monpriv->priv_list, &mon_priv_list);
 193        return nonseekable_open(inode, filp);
 194}
 195
 196static int monwrite_close(struct inode *inode, struct file *filp)
 197{
 198        struct mon_private *monpriv = filp->private_data;
 199        struct mon_buf *entry, *next;
 200
 201        list_for_each_entry_safe(entry, next, &monpriv->list, list) {
 202                if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
 203                        monwrite_diag(&entry->hdr, entry->data,
 204                                      APPLDATA_STOP_REC);
 205                mon_buf_count--;
 206                list_del(&entry->list);
 207                kfree(entry->data);
 208                kfree(entry);
 209        }
 210        list_del(&monpriv->priv_list);
 211        kfree(monpriv);
 212        return 0;
 213}
 214
 215static ssize_t monwrite_write(struct file *filp, const char __user *data,
 216                              size_t count, loff_t *ppos)
 217{
 218        struct mon_private *monpriv = filp->private_data;
 219        size_t len, written;
 220        void *to;
 221        int rc;
 222
 223        mutex_lock(&monpriv->thread_mutex);
 224        for (written = 0; written < count; ) {
 225                if (monpriv->hdr_to_read) {
 226                        len = min(count - written, monpriv->hdr_to_read);
 227                        to = (char *) &monpriv->hdr +
 228                                sizeof(monpriv->hdr) - monpriv->hdr_to_read;
 229                        if (copy_from_user(to, data + written, len)) {
 230                                rc = -EFAULT;
 231                                goto out_error;
 232                        }
 233                        monpriv->hdr_to_read -= len;
 234                        written += len;
 235                        if (monpriv->hdr_to_read > 0)
 236                                continue;
 237                        rc = monwrite_new_hdr(monpriv);
 238                        if (rc)
 239                                goto out_error;
 240                        monpriv->data_to_read = monpriv->current_buf ?
 241                                monpriv->current_buf->hdr.datalen : 0;
 242                }
 243
 244                if (monpriv->data_to_read) {
 245                        len = min(count - written, monpriv->data_to_read);
 246                        to = monpriv->current_buf->data +
 247                                monpriv->hdr.datalen - monpriv->data_to_read;
 248                        if (copy_from_user(to, data + written, len)) {
 249                                rc = -EFAULT;
 250                                goto out_error;
 251                        }
 252                        monpriv->data_to_read -= len;
 253                        written += len;
 254                        if (monpriv->data_to_read > 0)
 255                                continue;
 256                        rc = monwrite_new_data(monpriv);
 257                        if (rc)
 258                                goto out_error;
 259                }
 260                monpriv->hdr_to_read = sizeof(monpriv->hdr);
 261        }
 262        mutex_unlock(&monpriv->thread_mutex);
 263        return written;
 264
 265out_error:
 266        monpriv->data_to_read = 0;
 267        monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
 268        mutex_unlock(&monpriv->thread_mutex);
 269        return rc;
 270}
 271
 272static const struct file_operations monwrite_fops = {
 273        .owner   = THIS_MODULE,
 274        .open    = &monwrite_open,
 275        .release = &monwrite_close,
 276        .write   = &monwrite_write,
 277        .llseek  = noop_llseek,
 278};
 279
 280static struct miscdevice mon_dev = {
 281        .name   = "monwriter",
 282        .fops   = &monwrite_fops,
 283        .minor  = MISC_DYNAMIC_MINOR,
 284};
 285
 286/*
 287 * suspend/resume
 288 */
 289
 290static int monwriter_freeze(struct device *dev)
 291{
 292        struct mon_private *monpriv;
 293        struct mon_buf *monbuf;
 294
 295        list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
 296                list_for_each_entry(monbuf, &monpriv->list, list) {
 297                        if (monbuf->hdr.mon_function != MONWRITE_GEN_EVENT)
 298                                monwrite_diag(&monbuf->hdr, monbuf->data,
 299                                              APPLDATA_STOP_REC);
 300                }
 301        }
 302        return 0;
 303}
 304
 305static int monwriter_restore(struct device *dev)
 306{
 307        struct mon_private *monpriv;
 308        struct mon_buf *monbuf;
 309
 310        list_for_each_entry(monpriv, &mon_priv_list, priv_list) {
 311                list_for_each_entry(monbuf, &monpriv->list, list) {
 312                        if (monbuf->hdr.mon_function == MONWRITE_START_INTERVAL)
 313                                monwrite_diag(&monbuf->hdr, monbuf->data,
 314                                              APPLDATA_START_INTERVAL_REC);
 315                        if (monbuf->hdr.mon_function == MONWRITE_START_CONFIG)
 316                                monwrite_diag(&monbuf->hdr, monbuf->data,
 317                                              APPLDATA_START_CONFIG_REC);
 318                }
 319        }
 320        return 0;
 321}
 322
 323static int monwriter_thaw(struct device *dev)
 324{
 325        return monwriter_restore(dev);
 326}
 327
 328static const struct dev_pm_ops monwriter_pm_ops = {
 329        .freeze         = monwriter_freeze,
 330        .thaw           = monwriter_thaw,
 331        .restore        = monwriter_restore,
 332};
 333
 334static struct platform_driver monwriter_pdrv = {
 335        .driver = {
 336                .name   = "monwriter",
 337                .pm     = &monwriter_pm_ops,
 338        },
 339};
 340
 341static struct platform_device *monwriter_pdev;
 342
 343/*
 344 * module init/exit
 345 */
 346
 347static int __init mon_init(void)
 348{
 349        int rc;
 350
 351        if (!MACHINE_IS_VM)
 352                return -ENODEV;
 353
 354        rc = platform_driver_register(&monwriter_pdrv);
 355        if (rc)
 356                return rc;
 357
 358        monwriter_pdev = platform_device_register_simple("monwriter", -1, NULL,
 359                                                        0);
 360        if (IS_ERR(monwriter_pdev)) {
 361                rc = PTR_ERR(monwriter_pdev);
 362                goto out_driver;
 363        }
 364
 365        /*
 366         * misc_register() has to be the last action in module_init(), because
 367         * file operations will be available right after this.
 368         */
 369        rc = misc_register(&mon_dev);
 370        if (rc)
 371                goto out_device;
 372        return 0;
 373
 374out_device:
 375        platform_device_unregister(monwriter_pdev);
 376out_driver:
 377        platform_driver_unregister(&monwriter_pdrv);
 378        return rc;
 379}
 380
 381static void __exit mon_exit(void)
 382{
 383        misc_deregister(&mon_dev);
 384        platform_device_unregister(monwriter_pdev);
 385        platform_driver_unregister(&monwriter_pdrv);
 386}
 387
 388module_init(mon_init);
 389module_exit(mon_exit);
 390
 391module_param_named(max_bufs, mon_max_bufs, int, 0644);
 392MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers "
 393                 "that can be active at one time");
 394
 395MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
 396MODULE_DESCRIPTION("Character device driver for writing z/VM "
 397                   "APPLDATA monitor records.");
 398MODULE_LICENSE("GPL");
 399