Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.9 211 lines 5.1 kB view raw
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, "000000000", sizeof(sccb->evbuf.comp_id)); 141 sccb->evbuf.header.length = sizeof(sccb->evbuf); 142 sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header); 143 sccb->header.function_code = SCLP_NORMAL_WRITE; 144 rc = sclp_add_request(request); 145 if (rc) 146 return rc; 147 spin_lock_irqsave(&sclp_async_lock, flags); 148 while (request->status != SCLP_REQ_DONE && 149 request->status != SCLP_REQ_FAILED) { 150 sclp_sync_wait(); 151 } 152 spin_unlock_irqrestore(&sclp_async_lock, flags); 153 if (request->status != SCLP_REQ_DONE) 154 return -EIO; 155 rc = ((struct sclp_async_sccb *) 156 request->sccb)->header.response_code; 157 if (rc != 0x0020) 158 return -EIO; 159 if (evb->header.flags != 0x80) 160 return -EIO; 161 return rc; 162} 163 164static int __init sclp_async_init(void) 165{ 166 int rc; 167 168 rc = sclp_register(&sclp_async_register); 169 if (rc) 170 return rc; 171 rc = -EOPNOTSUPP; 172 if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) 173 goto out_sclp; 174 rc = -ENOMEM; 175 callhome_sysctl_header = register_sysctl_table(kern_dir_table); 176 if (!callhome_sysctl_header) 177 goto out_sclp; 178 request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); 179 sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 180 if (!request || !sccb) 181 goto out_mem; 182 rc = atomic_notifier_chain_register(&panic_notifier_list, 183 &call_home_panic_nb); 184 if (!rc) 185 goto out; 186out_mem: 187 kfree(request); 188 free_page((unsigned long) sccb); 189 unregister_sysctl_table(callhome_sysctl_header); 190out_sclp: 191 sclp_unregister(&sclp_async_register); 192out: 193 return rc; 194} 195module_init(sclp_async_init); 196 197static void __exit sclp_async_exit(void) 198{ 199 atomic_notifier_chain_unregister(&panic_notifier_list, 200 &call_home_panic_nb); 201 unregister_sysctl_table(callhome_sysctl_header); 202 sclp_unregister(&sclp_async_register); 203 free_page((unsigned long) sccb); 204 kfree(request); 205} 206module_exit(sclp_async_exit); 207 208MODULE_AUTHOR("Copyright IBM Corp. 2009"); 209MODULE_AUTHOR("Hans-Joachim Picht <hans@linux.vnet.ibm.com>"); 210MODULE_LICENSE("GPL"); 211MODULE_DESCRIPTION("SCLP Asynchronous Notification Records");