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