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 proc_handler_callhome(struct ctl_table *ctl, int write,
  68                                 void __user *buffer, size_t *count,
  69                                 loff_t *ppos)
  70{
  71        unsigned long val;
  72        int len, rc;
  73        char buf[3];
  74
  75        if (!*count || (*ppos && !write)) {
  76                *count = 0;
  77                return 0;
  78        }
  79        if (!write) {
  80                len = snprintf(buf, sizeof(buf), "%d\n", callhome_enabled);
  81                rc = copy_to_user(buffer, buf, sizeof(buf));
  82                if (rc != 0)
  83                        return -EFAULT;
  84        } else {
  85                len = *count;
  86                rc = kstrtoul_from_user(buffer, len, 0, &val);
  87                if (rc)
  88                        return rc;
  89                if (val != 0 && val != 1)
  90                        return -EINVAL;
  91                callhome_enabled = val;
  92        }
  93        *count = len;
  94        *ppos += len;
  95        return 0;
  96}
  97
  98static struct ctl_table callhome_table[] = {
  99        {
 100                .procname       = "callhome",
 101                .mode           = 0644,
 102                .proc_handler   = proc_handler_callhome,
 103        },
 104        {}
 105};
 106
 107static struct ctl_table kern_dir_table[] = {
 108        {
 109                .procname       = "kernel",
 110                .maxlen         = 0,
 111                .mode           = 0555,
 112                .child          = callhome_table,
 113        },
 114        {}
 115};
 116
 117/*
 118 * Function used to transfer asynchronous notification
 119 * records which waits for send completion
 120 */
 121static int sclp_async_send_wait(char *message)
 122{
 123        struct async_evbuf *evb;
 124        int rc;
 125        unsigned long flags;
 126
 127        if (!callhome_enabled)
 128                return 0;
 129        sccb->evbuf.header.type = EVTYP_ASYNC;
 130        sccb->evbuf.rtype = 0xA5;
 131        sccb->evbuf.otype = 0x00;
 132        evb = &sccb->evbuf;
 133        request->command = SCLP_CMDW_WRITE_EVENT_DATA;
 134        request->sccb = sccb;
 135        request->status = SCLP_REQ_FILLED;
 136        strncpy(sccb->evbuf.data, message, sizeof(sccb->evbuf.data));
 137        /*
 138         * Retain Queue
 139         * e.g. 5639CC140 500 Red Hat RHEL5 Linux for zSeries (RHEL AS)
 140         */
 141        strncpy(sccb->evbuf.comp_id, CONFIG_SCLP_ASYNC_ID,
 142                sizeof(sccb->evbuf.comp_id));
 143        sccb->evbuf.header.length = sizeof(sccb->evbuf);
 144        sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header);
 145        sccb->header.function_code = SCLP_NORMAL_WRITE;
 146        rc = sclp_add_request(request);
 147        if (rc)
 148                return rc;
 149        spin_lock_irqsave(&sclp_async_lock, flags);
 150        while (request->status != SCLP_REQ_DONE &&
 151                request->status != SCLP_REQ_FAILED) {
 152                 sclp_sync_wait();
 153        }
 154        spin_unlock_irqrestore(&sclp_async_lock, flags);
 155        if (request->status != SCLP_REQ_DONE)
 156                return -EIO;
 157        rc = ((struct sclp_async_sccb *)
 158               request->sccb)->header.response_code;
 159        if (rc != 0x0020)
 160                return -EIO;
 161        if (evb->header.flags != 0x80)
 162                return -EIO;
 163        return rc;
 164}
 165
 166static int __init sclp_async_init(void)
 167{
 168        int rc;
 169
 170        rc = sclp_register(&sclp_async_register);
 171        if (rc)
 172                return rc;
 173        rc = -EOPNOTSUPP;
 174        if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK))
 175                goto out_sclp;
 176        rc = -ENOMEM;
 177        callhome_sysctl_header = register_sysctl_table(kern_dir_table);
 178        if (!callhome_sysctl_header)
 179                goto out_sclp;
 180        request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
 181        sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 182        if (!request || !sccb)
 183                goto out_mem;
 184        rc = atomic_notifier_chain_register(&panic_notifier_list,
 185                                            &call_home_panic_nb);
 186        if (!rc)
 187                goto out;
 188out_mem:
 189        kfree(request);
 190        free_page((unsigned long) sccb);
 191        unregister_sysctl_table(callhome_sysctl_header);
 192out_sclp:
 193        sclp_unregister(&sclp_async_register);
 194out:
 195        return rc;
 196}
 197module_init(sclp_async_init);
 198
 199static void __exit sclp_async_exit(void)
 200{
 201        atomic_notifier_chain_unregister(&panic_notifier_list,
 202                                         &call_home_panic_nb);
 203        unregister_sysctl_table(callhome_sysctl_header);
 204        sclp_unregister(&sclp_async_register);
 205        free_page((unsigned long) sccb);
 206        kfree(request);
 207}
 208module_exit(sclp_async_exit);
 209
 210MODULE_AUTHOR("Copyright IBM Corp. 2009");
 211MODULE_AUTHOR("Hans-Joachim Picht <hans@linux.vnet.ibm.com>");
 212MODULE_LICENSE("GPL");
 213MODULE_DESCRIPTION("SCLP Asynchronous Notification Records");
 214