linux/drivers/s390/char/sclp_config.c
<<
>>
Prefs
   1/*
   2 *    Copyright IBM Corp. 2007
   3 *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
   4 */
   5
   6#define KMSG_COMPONENT "sclp_config"
   7#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
   8
   9#include <linux/init.h>
  10#include <linux/errno.h>
  11#include <linux/cpu.h>
  12#include <linux/device.h>
  13#include <linux/workqueue.h>
  14#include <linux/slab.h>
  15#include <linux/sysfs.h>
  16#include <asm/smp.h>
  17
  18#include "sclp.h"
  19
  20struct conf_mgm_data {
  21        u8 reserved;
  22        u8 ev_qualifier;
  23} __attribute__((packed));
  24
  25#define OFB_DATA_MAX 64
  26
  27struct sclp_ofb_evbuf {
  28        struct evbuf_header header;
  29        struct conf_mgm_data cm_data;
  30        char ev_data[OFB_DATA_MAX];
  31} __packed;
  32
  33struct sclp_ofb_sccb {
  34        struct sccb_header header;
  35        struct sclp_ofb_evbuf ofb_evbuf;
  36} __packed;
  37
  38#define EV_QUAL_CPU_CHANGE      1
  39#define EV_QUAL_CAP_CHANGE      3
  40#define EV_QUAL_OPEN4BUSINESS   5
  41
  42static struct work_struct sclp_cpu_capability_work;
  43static struct work_struct sclp_cpu_change_work;
  44
  45static void sclp_cpu_capability_notify(struct work_struct *work)
  46{
  47        int cpu;
  48        struct device *dev;
  49
  50        s390_adjust_jiffies();
  51        pr_info("CPU capability may have changed\n");
  52        get_online_cpus();
  53        for_each_online_cpu(cpu) {
  54                dev = get_cpu_device(cpu);
  55                kobject_uevent(&dev->kobj, KOBJ_CHANGE);
  56        }
  57        put_online_cpus();
  58}
  59
  60static void __ref sclp_cpu_change_notify(struct work_struct *work)
  61{
  62        smp_rescan_cpus();
  63}
  64
  65static void sclp_conf_receiver_fn(struct evbuf_header *evbuf)
  66{
  67        struct conf_mgm_data *cdata;
  68
  69        cdata = (struct conf_mgm_data *)(evbuf + 1);
  70        switch (cdata->ev_qualifier) {
  71        case EV_QUAL_CPU_CHANGE:
  72                schedule_work(&sclp_cpu_change_work);
  73                break;
  74        case EV_QUAL_CAP_CHANGE:
  75                schedule_work(&sclp_cpu_capability_work);
  76                break;
  77        }
  78}
  79
  80static struct sclp_register sclp_conf_register =
  81{
  82#ifdef CONFIG_SCLP_OFB
  83        .send_mask    = EVTYP_CONFMGMDATA_MASK,
  84#endif
  85        .receive_mask = EVTYP_CONFMGMDATA_MASK,
  86        .receiver_fn  = sclp_conf_receiver_fn,
  87};
  88
  89#ifdef CONFIG_SCLP_OFB
  90static int sclp_ofb_send_req(char *ev_data, size_t len)
  91{
  92        static DEFINE_MUTEX(send_mutex);
  93        struct sclp_ofb_sccb *sccb;
  94        int rc, response;
  95
  96        if (len > OFB_DATA_MAX)
  97                return -EINVAL;
  98        sccb = (struct sclp_ofb_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
  99        if (!sccb)
 100                return -ENOMEM;
 101        /* Setup SCCB for Control-Program Identification */
 102        sccb->header.length = sizeof(struct sclp_ofb_sccb);
 103        sccb->ofb_evbuf.header.length = sizeof(struct sclp_ofb_evbuf);
 104        sccb->ofb_evbuf.header.type = EVTYP_CONFMGMDATA;
 105        sccb->ofb_evbuf.cm_data.ev_qualifier = EV_QUAL_OPEN4BUSINESS;
 106        memcpy(sccb->ofb_evbuf.ev_data, ev_data, len);
 107
 108        if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK))
 109                pr_warn("SCLP receiver did not register to receive "
 110                        "Configuration Management Data Events.\n");
 111
 112        mutex_lock(&send_mutex);
 113        rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 114        mutex_unlock(&send_mutex);
 115        if (rc)
 116                goto out;
 117        response = sccb->header.response_code;
 118        if (response != 0x0020) {
 119                pr_err("Open for Business request failed with response code "
 120                       "0x%04x\n", response);
 121                rc = -EIO;
 122        }
 123out:
 124        free_page((unsigned long)sccb);
 125        return rc;
 126}
 127
 128static ssize_t sysfs_ofb_data_write(struct file *filp, struct kobject *kobj,
 129                                    struct bin_attribute *bin_attr,
 130                                    char *buf, loff_t off, size_t count)
 131{
 132        int rc;
 133
 134        rc = sclp_ofb_send_req(buf, count);
 135        return rc ?: count;
 136}
 137
 138static struct bin_attribute ofb_bin_attr = {
 139        .attr = {
 140                .name = "event_data",
 141                .mode = S_IWUSR,
 142        },
 143        .write = sysfs_ofb_data_write,
 144};
 145#endif
 146
 147static int __init sclp_ofb_setup(void)
 148{
 149#ifdef CONFIG_SCLP_OFB
 150        struct kset *ofb_kset;
 151        int rc;
 152
 153        ofb_kset = kset_create_and_add("ofb", NULL, firmware_kobj);
 154        if (!ofb_kset)
 155                return -ENOMEM;
 156        rc = sysfs_create_bin_file(&ofb_kset->kobj, &ofb_bin_attr);
 157        if (rc) {
 158                kset_unregister(ofb_kset);
 159                return rc;
 160        }
 161#endif
 162        return 0;
 163}
 164
 165static int __init sclp_conf_init(void)
 166{
 167        int rc;
 168
 169        INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
 170        INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
 171        rc = sclp_register(&sclp_conf_register);
 172        if (rc)
 173                return rc;
 174        return sclp_ofb_setup();
 175}
 176
 177__initcall(sclp_conf_init);
 178