forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Hans-Joachim Picht <[email protected]> Signed-off-by: Martin Schwidefsky <[email protected]>
- Loading branch information
Hans-Joachim Picht
authored and
Martin Schwidefsky
committed
Sep 11, 2009
1 parent
275c340
commit c114728
Showing
5 changed files
with
254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
/* | ||
* Enable Asynchronous Notification via SCLP. | ||
* | ||
* Copyright IBM Corp. 2009 | ||
* Author(s): Hans-Joachim Picht <[email protected]> | ||
* | ||
*/ | ||
|
||
#include <linux/init.h> | ||
#include <linux/module.h> | ||
#include <linux/device.h> | ||
#include <linux/stat.h> | ||
#include <linux/string.h> | ||
#include <linux/ctype.h> | ||
#include <linux/kmod.h> | ||
#include <linux/err.h> | ||
#include <linux/errno.h> | ||
#include <linux/proc_fs.h> | ||
#include <linux/sysctl.h> | ||
#include <linux/utsname.h> | ||
#include "sclp.h" | ||
|
||
static int callhome_enabled; | ||
static struct sclp_req *request; | ||
static struct sclp_async_sccb *sccb; | ||
static int sclp_async_send_wait(char *message); | ||
static struct ctl_table_header *callhome_sysctl_header; | ||
static DEFINE_SPINLOCK(sclp_async_lock); | ||
static char nodename[64]; | ||
#define SCLP_NORMAL_WRITE 0x00 | ||
|
||
struct async_evbuf { | ||
struct evbuf_header header; | ||
u64 reserved; | ||
u8 rflags; | ||
u8 empty; | ||
u8 rtype; | ||
u8 otype; | ||
char comp_id[12]; | ||
char data[3000]; /* there is still some space left */ | ||
} __attribute__((packed)); | ||
|
||
struct sclp_async_sccb { | ||
struct sccb_header header; | ||
struct async_evbuf evbuf; | ||
} __attribute__((packed)); | ||
|
||
static struct sclp_register sclp_async_register = { | ||
.send_mask = EVTYP_ASYNC_MASK, | ||
}; | ||
|
||
static int call_home_on_panic(struct notifier_block *self, | ||
unsigned long event, void *data) | ||
{ | ||
strncat(data, nodename, strlen(nodename)); | ||
sclp_async_send_wait(data); | ||
return NOTIFY_DONE; | ||
} | ||
|
||
static struct notifier_block call_home_panic_nb = { | ||
.notifier_call = call_home_on_panic, | ||
.priority = INT_MAX, | ||
}; | ||
|
||
static int proc_handler_callhome(ctl_table *ctl, int write, struct file *filp, | ||
void __user *buffer, size_t *count, | ||
loff_t *ppos) | ||
{ | ||
unsigned long val; | ||
int len, rc; | ||
char buf[2]; | ||
|
||
if (!*count | (*ppos && !write)) { | ||
*count = 0; | ||
return 0; | ||
} | ||
if (!write) { | ||
len = sprintf(buf, "%d\n", callhome_enabled); | ||
buf[len] = '\0'; | ||
rc = copy_to_user(buffer, buf, sizeof(buf)); | ||
if (rc != 0) | ||
return -EFAULT; | ||
} else { | ||
len = *count; | ||
rc = copy_from_user(buf, buffer, sizeof(buf)); | ||
if (rc != 0) | ||
return -EFAULT; | ||
if (strict_strtoul(buf, 0, &val) != 0) | ||
return -EINVAL; | ||
if (val != 0 && val != 1) | ||
return -EINVAL; | ||
callhome_enabled = val; | ||
} | ||
*count = len; | ||
*ppos += len; | ||
return 0; | ||
} | ||
|
||
static struct ctl_table callhome_table[] = { | ||
{ | ||
.procname = "callhome", | ||
.mode = 0644, | ||
.proc_handler = &proc_handler_callhome, | ||
}, | ||
{ .ctl_name = 0 } | ||
}; | ||
|
||
static struct ctl_table kern_dir_table[] = { | ||
{ | ||
.ctl_name = CTL_KERN, | ||
.procname = "kernel", | ||
.maxlen = 0, | ||
.mode = 0555, | ||
.child = callhome_table, | ||
}, | ||
{ .ctl_name = 0 } | ||
}; | ||
|
||
/* | ||
* Function used to transfer asynchronous notification | ||
* records which waits for send completion | ||
*/ | ||
static int sclp_async_send_wait(char *message) | ||
{ | ||
struct async_evbuf *evb; | ||
int rc; | ||
unsigned long flags; | ||
|
||
if (!callhome_enabled) | ||
return 0; | ||
sccb->evbuf.header.type = EVTYP_ASYNC; | ||
sccb->evbuf.rtype = 0xA5; | ||
sccb->evbuf.otype = 0x00; | ||
evb = &sccb->evbuf; | ||
request->command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
request->sccb = sccb; | ||
request->status = SCLP_REQ_FILLED; | ||
strncpy(sccb->evbuf.data, message, sizeof(sccb->evbuf.data)); | ||
/* | ||
* Retain Queue | ||
* e.g. 5639CC140 500 Red Hat RHEL5 Linux for zSeries (RHEL AS) | ||
*/ | ||
strncpy(sccb->evbuf.comp_id, "000000000", sizeof(sccb->evbuf.comp_id)); | ||
sccb->evbuf.header.length = sizeof(sccb->evbuf); | ||
sccb->header.length = sizeof(sccb->evbuf) + sizeof(sccb->header); | ||
sccb->header.function_code = SCLP_NORMAL_WRITE; | ||
rc = sclp_add_request(request); | ||
if (rc) | ||
return rc; | ||
spin_lock_irqsave(&sclp_async_lock, flags); | ||
while (request->status != SCLP_REQ_DONE && | ||
request->status != SCLP_REQ_FAILED) { | ||
sclp_sync_wait(); | ||
} | ||
spin_unlock_irqrestore(&sclp_async_lock, flags); | ||
if (request->status != SCLP_REQ_DONE) | ||
return -EIO; | ||
rc = ((struct sclp_async_sccb *) | ||
request->sccb)->header.response_code; | ||
if (rc != 0x0020) | ||
return -EIO; | ||
if (evb->header.flags != 0x80) | ||
return -EIO; | ||
return rc; | ||
} | ||
|
||
static int __init sclp_async_init(void) | ||
{ | ||
int rc; | ||
|
||
rc = sclp_register(&sclp_async_register); | ||
if (rc) | ||
return rc; | ||
callhome_sysctl_header = register_sysctl_table(kern_dir_table); | ||
if (!callhome_sysctl_header) { | ||
rc = -ENOMEM; | ||
goto out_sclp; | ||
} | ||
if (!(sclp_async_register.sclp_receive_mask & EVTYP_ASYNC_MASK)) { | ||
rc = -EOPNOTSUPP; | ||
goto out_sclp; | ||
} | ||
rc = -ENOMEM; | ||
request = kzalloc(sizeof(struct sclp_req), GFP_KERNEL); | ||
if (!request) | ||
goto out_sys; | ||
sccb = (struct sclp_async_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
if (!sccb) | ||
goto out_mem; | ||
rc = atomic_notifier_chain_register(&panic_notifier_list, | ||
&call_home_panic_nb); | ||
if (rc) | ||
goto out_mem; | ||
|
||
strncpy(nodename, init_utsname()->nodename, 64); | ||
return 0; | ||
|
||
out_mem: | ||
kfree(request); | ||
free_page((unsigned long) sccb); | ||
out_sys: | ||
unregister_sysctl_table(callhome_sysctl_header); | ||
out_sclp: | ||
sclp_unregister(&sclp_async_register); | ||
return rc; | ||
|
||
} | ||
module_init(sclp_async_init); | ||
|
||
static void __exit sclp_async_exit(void) | ||
{ | ||
atomic_notifier_chain_unregister(&panic_notifier_list, | ||
&call_home_panic_nb); | ||
unregister_sysctl_table(callhome_sysctl_header); | ||
sclp_unregister(&sclp_async_register); | ||
free_page((unsigned long) sccb); | ||
kfree(request); | ||
} | ||
module_exit(sclp_async_exit); | ||
|
||
MODULE_AUTHOR("Copyright IBM Corp. 2009"); | ||
MODULE_AUTHOR("Hans-Joachim Picht <[email protected]>"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_DESCRIPTION("SCLP Asynchronous Notification Records"); |