linux/drivers/s390/char/sclp_async.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Enable Asynchronous Notification via SCLP.
   4 *
   5 * Copyright IBM Corp. 2009
   6 * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
   7 *
   8 */
   9
  10#include <linux/init.h>
  11#include <linux/module.h>
  12#include <linux/device.h>
  13#include <linux/stat.h>
  14#include <linux/string.h>
  15#include <linux/slab.h>
  16#include <linux/ctype.h>
  17#include <linux/kmod.h>
  18#include <linux/err.h>
  19#include <linux/errno.h>
  20#include <linux/proc_fs.h>
  21#include <linux/sysctl.h>
  22#include <linux/utsname.h>
  23#include "sclp.h"
  24
  25static int callhome_enabled;
  26static struct sclp_req *request;
  27static struct sclp_async_sccb *sccb;
  28static int sclp_async_send_wait(char *message);
  29static struct ctl_table_header *callhome_sysctl_header;
  30static DEFINE_SPINLOCK(sclp_async_lock);
  31#define SCLP_NORMAL_WRITE       0x00
  32
  33struct async_evbuf {
  34        struct evbuf_header header;
  35        u64 reserved;
  36        u8 rflags;
  37        u8 empty;
  38        u8 rtype;
  39        u8 otype;
  40        char comp_id[12];
  41        char data[3000]; /* there is still some space left */
  42} __attribute__((packed));
  43
  44struct sclp_async_sccb {
  45        struct sccb_header header;
  46        struct async_evbuf evbuf;
  47} __attribute__((packed));
  48
  49static struct sclp_register sclp_async_register = {
  50        .send_mask = EVTYP_ASYNC_MASK,
  51};
  52
  53static int call_home_on_panic(struct notifier_block *self,
  54                              unsigned long event, void *data)
  55{
  56        strncat(data, init_utsname()->nodename,
  57                sizeof(init_utsname()->nodename));
  58        sclp_async_send_wait(data);
  59        return NOTIFY_DONE;
  60}
  61
  62static struct notifier_block call_home_panic_nb = {
  63        .notifier_call = call_home_on_panic,
  64        .priority = INT_MAX,
  65};
  66
  67static int zero;
  68static int one = 1;
  69
  70static struct ctl_table callhome_table[] = {
  71        {
  72                .procname       = "callhome",
  73                .data           = &callhome_enabled,
  74                .maxlen         = sizeof(int),
  75                .mode           = 0644,
  76                .proc_handler   = proc_dointvec_minmax,
  77                .extra1         = &zero,
  78                .extra2         = &one,
  79        },
  80        {}
  81};
  82
  83static struct ctl_table kern_dir_table[] = {
  84        {
  85                .procname       = "kernel",
  86                .maxlen         = 0,
  87                .mode           = 0555,
  88                .child          = callhome_table,
  89        },
  90        {}
  91};
  92
  93/*
  94 * Function used to transfer asynchronous notification
  95 * records which waits for send completion
  96 */
  97static int sclp_async_send_wait(char *message)
  98{
  99        struct async_evbuf *evb;
 100        int rc;
 101        unsigned long flags;
 102
 103        if (!callhome_enabled)
 104                return 0;
 105        sccb->evbuf.header.type = EVTYP_ASYNC;
 106        sccb->evbuf.rtype = 0xA5;
 107        sccb->evbuf.otype = 0x00;
 108        evb = &sccb->evbuf;
 109        request->command = SCLP_CMDW_WRITE_EVENT_DATA;
 110        request->sccb = sccb;
 111        request->status = SCLP_REQ_FILLED;
 112        strncpy(sccb->evbuf.data, message, sizeof(sccb->evbuf.data));
 113        /*
 114         * Retain Queue
 115         * e.g. 5639CC140 500 Red Hat RHEL5 Linux for zSeries (RHEL AS)
 116         */
 117        strncpy(sccb->evbuf.comp_id, CONFIG_SCLP_ASYNC_ID,
 118                sizeof(sccb->evbuf.comp_id));
 119        sccb->evbuf.header.length = sizeof(sccb->evbuf);
 120        sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header);
 121        sccb->header.function_code = SCLP_NORMAL_WRITE;
 122        rc = sclp_add_request(request);
 123        if (rc)
 124                return rc;
 125        spin_lock_irqsave(&sclp_async_lock, flags);
 126        while (request->status != SCLP_REQ_DONE &&
 127                request->status != SCLP_REQ_FAILED) {
 128                 sclp_sync_wait();
 129        }
 130        spin_unlock_irqrestore(&sclp_async_lock, flags);
 131        if (request->status != SCLP_REQ_DONE)
 132                return -EIO;
 133        rc = ((struct sclp_async_sccb *)
 134               request->sccb)->header.response_code;
 135        if (rc != 0x0020)
 136                return -EIO;
 137        if (evb->header.flags != 0x80)
 138                return -EIO;
 139        return rc;
 140}
 141
 142static int __init sclp_async_init(void)
 143{
 144        int rc;
 145
 146        rc = sclp_register(&sclp_async_register);
 147        if (rc)
 148                return rc;
 149        rc = -EOPNOTSUPP;
 150        if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK))
 151                goto out_sclp;
 152        rc = -ENOMEM;
 153        callhome_sysctl_header = register_sysctl_table(kern_dir_table);
 154        if (!callhome_sysctl_header)
 155                goto out_sclp;
 156        request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
 157        sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 158        if (!request || !sccb)
 159                goto out_mem;
 160        rc = atomic_notifier_chain_register(&panic_notifier_list,
 161                                            &call_home_panic_nb);
 162        if (!rc)
 163                goto out;
 164out_mem:
 165        kfree(request);
 166        free_page((unsigned long) sccb);
 167        unregister_sysctl_table(callhome_sysctl_header);
 168out_sclp:
 169        sclp_unregister(&sclp_async_register);
 170out:
 171        return rc;
 172}
 173module_init(sclp_async_init);
 174
 175static void __exit sclp_async_exit(void)
 176{
 177        atomic_notifier_chain_unregister(&panic_notifier_list,
 178                                         &call_home_panic_nb);
 179        unregister_sysctl_table(callhome_sysctl_header);
 180        sclp_unregister(&sclp_async_register);
 181        free_page((unsigned long) sccb);
 182        kfree(request);
 183}
 184module_exit(sclp_async_exit);
 185
 186MODULE_AUTHOR("Copyright IBM Corp. 2009");
 187MODULE_AUTHOR("Hans-Joachim Picht <hans@linux.vnet.ibm.com>");
 188MODULE_LICENSE("GPL");
 189MODULE_DESCRIPTION("SCLP Asynchronous Notification Records");
 190