diff options
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/ipmi_devintf.c | 210 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 48 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 137 |
3 files changed, 352 insertions, 43 deletions
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 6dc765dc541..e0a53570fea 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -45,6 +45,7 @@ #include <asm/semaphore.h> #include <linux/init.h> #include <linux/device.h> +#include <linux/compat.h> #define IPMI_DEVINTF_VERSION "v33" @@ -500,10 +501,205 @@ static int ipmi_ioctl(struct inode *inode, return rv; } +#ifdef CONFIG_COMPAT + +/* + * The following code contains code for supporting 32-bit compatible + * ioctls on 64-bit kernels. This allows running 32-bit apps on the + * 64-bit kernel + */ +#define COMPAT_IPMICTL_SEND_COMMAND \ + _IOR(IPMI_IOC_MAGIC, 13, struct compat_ipmi_req) +#define COMPAT_IPMICTL_SEND_COMMAND_SETTIME \ + _IOR(IPMI_IOC_MAGIC, 21, struct compat_ipmi_req_settime) +#define COMPAT_IPMICTL_RECEIVE_MSG \ + _IOWR(IPMI_IOC_MAGIC, 12, struct compat_ipmi_recv) +#define COMPAT_IPMICTL_RECEIVE_MSG_TRUNC \ + _IOWR(IPMI_IOC_MAGIC, 11, struct compat_ipmi_recv) + +struct compat_ipmi_msg { + u8 netfn; + u8 cmd; + u16 data_len; + compat_uptr_t data; +}; + +struct compat_ipmi_req { + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_recv { + compat_int_t recv_type; + compat_uptr_t addr; + compat_uint_t addr_len; + compat_long_t msgid; + struct compat_ipmi_msg msg; +}; + +struct compat_ipmi_req_settime { + struct compat_ipmi_req req; + compat_int_t retries; + compat_uint_t retry_time_ms; +}; + +/* + * Define some helper functions for copying IPMI data + */ +static long get_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->netfn, &p32->netfn) || + __get_user(p64->cmd, &p32->cmd) || + __get_user(p64->data_len, &p32->data_len) || + __get_user(tmp, &p32->data)) + return -EFAULT; + p64->data = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_msg(struct ipmi_msg *p64, + struct compat_ipmi_msg __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->netfn, &p32->netfn) || + __put_user(p64->cmd, &p32->cmd) || + __put_user(p64->data_len, &p32->data_len)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_req(struct ipmi_req *p64, + struct compat_ipmi_req __user *p32) +{ + + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long get_compat_ipmi_req_settime(struct ipmi_req_settime *p64, + struct compat_ipmi_req_settime __user *p32) +{ + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + get_compat_ipmi_req(&p64->req, &p32->req) || + __get_user(p64->retries, &p32->retries) || + __get_user(p64->retry_time_ms, &p32->retry_time_ms)) + return -EFAULT; + return 0; +} + +static long get_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + compat_uptr_t tmp; + + if (!access_ok(VERIFY_READ, p32, sizeof(*p32)) || + __get_user(p64->recv_type, &p32->recv_type) || + __get_user(tmp, &p32->addr) || + __get_user(p64->addr_len, &p32->addr_len) || + __get_user(p64->msgid, &p32->msgid) || + get_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + p64->addr = compat_ptr(tmp); + return 0; +} + +static long put_compat_ipmi_recv(struct ipmi_recv *p64, + struct compat_ipmi_recv __user *p32) +{ + if (!access_ok(VERIFY_WRITE, p32, sizeof(*p32)) || + __put_user(p64->recv_type, &p32->recv_type) || + __put_user(p64->addr_len, &p32->addr_len) || + __put_user(p64->msgid, &p32->msgid) || + put_compat_ipmi_msg(&p64->msg, &p32->msg)) + return -EFAULT; + return 0; +} + +/* + * Handle compatibility ioctls + */ +static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc; + struct ipmi_file_private *priv = filep->private_data; + + switch(cmd) { + case COMPAT_IPMICTL_SEND_COMMAND: + { + struct ipmi_req rp; + + if (get_compat_ipmi_req(&rp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &rp, + priv->default_retries, + priv->default_retry_time_ms); + } + case COMPAT_IPMICTL_SEND_COMMAND_SETTIME: + { + struct ipmi_req_settime sp; + + if (get_compat_ipmi_req_settime(&sp, compat_ptr(arg))) + return -EFAULT; + + return handle_send_req(priv->user, &sp.req, + sp.retries, sp.retry_time_ms); + } + case COMPAT_IPMICTL_RECEIVE_MSG: + case COMPAT_IPMICTL_RECEIVE_MSG_TRUNC: + { + struct ipmi_recv *precv64, recv64; + + if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + precv64 = compat_alloc_user_space(sizeof(recv64)); + if (copy_to_user(precv64, &recv64, sizeof(recv64))) + return -EFAULT; + + rc = ipmi_ioctl(filep->f_dentry->d_inode, filep, + ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) + ? IPMICTL_RECEIVE_MSG + : IPMICTL_RECEIVE_MSG_TRUNC), + (long) precv64); + if (rc != 0) + return rc; + + if (copy_from_user(&recv64, precv64, sizeof(recv64))) + return -EFAULT; + + if (put_compat_ipmi_recv(&recv64, compat_ptr(arg))) + return -EFAULT; + + return rc; + } + default: + return ipmi_ioctl(filep->f_dentry->d_inode, filep, cmd, arg); + } +} +#endif static struct file_operations ipmi_fops = { .owner = THIS_MODULE, .ioctl = ipmi_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_ipmi_ioctl, +#endif .open = ipmi_open, .release = ipmi_release, .fasync = ipmi_fasync, @@ -520,7 +716,7 @@ MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" " interface. Other values will set the major device number" " to that value."); -static struct class_simple *ipmi_class; +static struct class *ipmi_class; static void ipmi_new_smi(int if_num) { @@ -529,12 +725,12 @@ static void ipmi_new_smi(int if_num) devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, "ipmidev/%d", if_num); - class_simple_device_add(ipmi_class, dev, NULL, "ipmi%d", if_num); + class_device_create(ipmi_class, dev, NULL, "ipmi%d", if_num); } static void ipmi_smi_gone(int if_num) { - class_simple_device_remove(MKDEV(ipmi_major, if_num)); + class_device_destroy(ipmi_class, MKDEV(ipmi_major, if_num)); devfs_remove("ipmidev/%d", if_num); } @@ -555,7 +751,7 @@ static __init int init_ipmi_devintf(void) printk(KERN_INFO "ipmi device interface version " IPMI_DEVINTF_VERSION "\n"); - ipmi_class = class_simple_create(THIS_MODULE, "ipmi"); + ipmi_class = class_create(THIS_MODULE, "ipmi"); if (IS_ERR(ipmi_class)) { printk(KERN_ERR "ipmi: can't register device class\n"); return PTR_ERR(ipmi_class); @@ -563,7 +759,7 @@ static __init int init_ipmi_devintf(void) rv = register_chrdev(ipmi_major, DEVICE_NAME, &ipmi_fops); if (rv < 0) { - class_simple_destroy(ipmi_class); + class_destroy(ipmi_class); printk(KERN_ERR "ipmi: can't get major %d\n", ipmi_major); return rv; } @@ -577,7 +773,7 @@ static __init int init_ipmi_devintf(void) rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) { unregister_chrdev(ipmi_major, DEVICE_NAME); - class_simple_destroy(ipmi_class); + class_destroy(ipmi_class); printk(KERN_WARNING "ipmi: can't register smi watcher\n"); return rv; } @@ -588,7 +784,7 @@ module_init(init_ipmi_devintf); static __exit void cleanup_ipmi(void) { - class_simple_destroy(ipmi_class); + class_destroy(ipmi_class); ipmi_smi_watcher_unregister(&smi_watcher); devfs_remove(DEVICE_NAME); unregister_chrdev(ipmi_major, DEVICE_NAME); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index d7fb452af7f..1813d0d198f 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -54,7 +54,9 @@ static int ipmi_init_msghandler(void); static int initialized = 0; -static struct proc_dir_entry *proc_ipmi_root = NULL; +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *proc_ipmi_root = NULL; +#endif /* CONFIG_PROC_FS */ #define MAX_EVENTS_IN_QUEUE 25 @@ -124,11 +126,13 @@ struct ipmi_channel unsigned char protocol; }; +#ifdef CONFIG_PROC_FS struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; +#endif #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 8 @@ -156,10 +160,13 @@ struct ipmi_smi struct ipmi_smi_handlers *handlers; void *send_info; +#ifdef CONFIG_PROC_FS /* A list of proc entries for this interface. This does not need a lock, only one thread creates it and only one thread destroys it. */ + spinlock_t proc_entry_lock; struct ipmi_proc_entry *proc_entries; +#endif /* A table of sequence numbers for this interface. We use the sequence numbers for IPMB messages that go out of the @@ -641,7 +648,7 @@ int ipmi_create_user(unsigned int if_num, return -ENOMEM; down_read(&interfaces_sem); - if ((if_num > MAX_IPMI_INTERFACES) || ipmi_interfaces[if_num] == NULL) + if ((if_num >= MAX_IPMI_INTERFACES) || ipmi_interfaces[if_num] == NULL) { rv = -EINVAL; goto out_unlock; @@ -1470,8 +1477,9 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, read_proc_t *read_proc, write_proc_t *write_proc, void *data, struct module *owner) { - struct proc_dir_entry *file; int rv = 0; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *file; struct ipmi_proc_entry *entry; /* Create a list element. */ @@ -1497,10 +1505,13 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, file->write_proc = write_proc; file->owner = owner; + spin_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; smi->proc_entries = entry; + spin_unlock(&smi->proc_entry_lock); } +#endif /* CONFIG_PROC_FS */ return rv; } @@ -1509,6 +1520,7 @@ static int add_proc_entries(ipmi_smi_t smi, int num) { int rv = 0; +#ifdef CONFIG_PROC_FS sprintf(smi->proc_dir_name, "%d", num); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) @@ -1531,14 +1543,17 @@ static int add_proc_entries(ipmi_smi_t smi, int num) rv = ipmi_smi_add_proc_entry(smi, "version", version_file_read_proc, NULL, smi, THIS_MODULE); +#endif /* CONFIG_PROC_FS */ return rv; } static void remove_proc_entries(ipmi_smi_t smi) { +#ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; + spin_lock(&smi->proc_entry_lock); while (smi->proc_entries) { entry = smi->proc_entries; smi->proc_entries = entry->next; @@ -1547,7 +1562,9 @@ static void remove_proc_entries(ipmi_smi_t smi) kfree(entry->name); kfree(entry); } + spin_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); +#endif /* CONFIG_PROC_FS */ } static int @@ -1694,6 +1711,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, new_intf->seq_table[j].seqid = 0; } new_intf->curr_seq = 0; +#ifdef CONFIG_PROC_FS + spin_lock_init(&(new_intf->proc_entry_lock)); +#endif spin_lock_init(&(new_intf->waiting_msgs_lock)); INIT_LIST_HEAD(&(new_intf->waiting_msgs)); spin_lock_init(&(new_intf->events_lock)); @@ -2747,16 +2767,13 @@ static struct timer_list ipmi_timer; the queue and this silliness can go away. */ #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) -static volatile int stop_operation = 0; -static volatile int timer_stopped = 0; +static atomic_t stop_operation; static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME; static void ipmi_timeout(unsigned long data) { - if (stop_operation) { - timer_stopped = 1; + if (atomic_read(&stop_operation)) return; - } ticks_to_req_ev--; if (ticks_to_req_ev == 0) { @@ -2766,8 +2783,7 @@ static void ipmi_timeout(unsigned long data) ipmi_timeout_handler(IPMI_TIMEOUT_TIME); - ipmi_timer.expires += IPMI_TIMEOUT_JIFFIES; - add_timer(&ipmi_timer); + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } @@ -3089,6 +3105,7 @@ static int ipmi_init_msghandler(void) ipmi_interfaces[i] = NULL; } +#ifdef CONFIG_PROC_FS proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { printk(KERN_ERR PFX "Unable to create IPMI proc dir"); @@ -3096,6 +3113,7 @@ static int ipmi_init_msghandler(void) } proc_ipmi_root->owner = THIS_MODULE; +#endif /* CONFIG_PROC_FS */ init_timer(&ipmi_timer); ipmi_timer.data = 0; @@ -3130,13 +3148,12 @@ static __exit void cleanup_ipmi(void) /* Tell the timer to stop, then wait for it to stop. This avoids problems with race conditions removing the timer here. */ - stop_operation = 1; - while (!timer_stopped) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } + atomic_inc(&stop_operation); + del_timer_sync(&ipmi_timer); +#ifdef CONFIG_PROC_FS remove_proc_entry(proc_ipmi_root->name, &proc_root); +#endif /* CONFIG_PROC_FS */ initialized = 0; @@ -3177,4 +3194,5 @@ EXPORT_SYMBOL(ipmi_get_my_address); EXPORT_SYMBOL(ipmi_set_my_LUN); EXPORT_SYMBOL(ipmi_get_my_LUN); EXPORT_SYMBOL(ipmi_smi_add_proc_entry); +EXPORT_SYMBOL(proc_ipmi_root); EXPORT_SYMBOL(ipmi_user_set_run_to_completion); diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index cb5cdc6f14b..f951c30236c 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -31,10 +31,13 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <asm/semaphore.h> -#include <linux/kdev_t.h> +#include <linux/config.h> #include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/proc_fs.h> #include <linux/string.h> +#include <linux/completion.h> +#include <linux/kdev_t.h> #include <linux/ipmi.h> #include <linux/ipmi_smi.h> @@ -44,6 +47,18 @@ /* Where to we insert our poweroff function? */ extern void (*pm_power_off)(void); +/* Definitions for controlling power off (if the system supports it). It + * conveniently matches the IPMI chassis control values. */ +#define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ +#define IPMI_CHASSIS_POWER_CYCLE 0x02 /* power cycle */ + +/* the IPMI data command */ +static int poweroff_control = IPMI_CHASSIS_POWER_DOWN; + +/* parameter definition to allow user to flag power cycle */ +module_param(poweroff_control, int, IPMI_CHASSIS_POWER_DOWN); +MODULE_PARM_DESC(poweroff_control, " Set to 2 to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); + /* Stuff from the get device id command. */ static unsigned int mfg_id; static unsigned int prod_id; @@ -75,10 +90,10 @@ static struct ipmi_recv_msg halt_recv_msg = static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) { - struct semaphore *sem = recv_msg->user_msg_data; + struct completion *comp = recv_msg->user_msg_data; - if (sem) - up(sem); + if (comp) + complete(comp); } static struct ipmi_user_hndl ipmi_poweroff_handler = @@ -91,27 +106,27 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; - struct semaphore sem; + int rv; + struct completion comp; - sema_init (&sem, 0); + init_completion(&comp); - rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &sem, + rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, &comp, &halt_smi_msg, &halt_recv_msg, 0); if (rv) return rv; - down (&sem); + wait_for_completion(&comp); return halt_recv_msg.msg.data[0]; } -/* We are in run-to-completion mode, no semaphore is desired. */ +/* We are in run-to-completion mode, no completion is desired. */ static int ipmi_request_in_rc_mode(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { - int rv; + int rv; rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, &halt_smi_msg, &halt_recv_msg, 0); @@ -349,26 +364,38 @@ static void ipmi_poweroff_chassis (ipmi_user_t user) smi_addr.channel = IPMI_BMC_CHANNEL; smi_addr.lun = 0; - printk(KERN_INFO PFX "Powering down via IPMI chassis control command\n"); + powercyclefailed: + printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", + ((poweroff_control != IPMI_CHASSIS_POWER_CYCLE) ? "down" : "cycle")); /* * Power down */ send_msg.netfn = IPMI_NETFN_CHASSIS_REQUEST; send_msg.cmd = IPMI_CHASSIS_CONTROL_CMD; - data[0] = 0; /* Power down */ + data[0] = poweroff_control; send_msg.data = data; send_msg.data_len = sizeof(data); rv = ipmi_request_in_rc_mode(user, (struct ipmi_addr *) &smi_addr, &send_msg); if (rv) { - printk(KERN_ERR PFX "Unable to send chassis powerdown message," - " IPMI error 0x%x\n", rv); - goto out; + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + /* power cycle failed, default to power down */ + printk(KERN_ERR PFX "Unable to send chassis power " \ + "cycle message, IPMI error 0x%x\n", rv); + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + goto powercyclefailed; + + case IPMI_CHASSIS_POWER_DOWN: + default: + printk(KERN_ERR PFX "Unable to send chassis power " \ + "down message, IPMI error 0x%x\n", rv); + break; + } } - out: return; } @@ -430,7 +457,8 @@ static void ipmi_po_new_smi(int if_num) if (ready) return; - rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, + &ipmi_user); if (rv) { printk(KERN_ERR PFX "could not create IPMI user, error %d\n", rv); @@ -509,21 +537,84 @@ static struct ipmi_smi_watcher smi_watcher = }; +#ifdef CONFIG_PROC_FS +/* displays properties to proc */ +static int proc_read_chassctrl(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + return sprintf(page, "%d\t[ 0=powerdown 2=powercycle ]\n", + poweroff_control); +} + +/* process property writes from proc */ +static int proc_write_chassctrl(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int rv = count; + unsigned int newval = 0; + + sscanf(buffer, "%d", &newval); + switch (newval) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "power cycle is now enabled\n"); + poweroff_control = newval; + break; + + case IPMI_CHASSIS_POWER_DOWN: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + + default: + rv = -EINVAL; + break; + } + + return rv; +} +#endif /* CONFIG_PROC_FS */ + /* * Startup and shutdown functions. */ static int ipmi_poweroff_init (void) { - int rv; + int rv; + struct proc_dir_entry *file; printk ("Copyright (C) 2004 MontaVista Software -" " IPMI Powerdown via sys_reboot version " IPMI_POWEROFF_VERSION ".\n"); + switch (poweroff_control) { + case IPMI_CHASSIS_POWER_CYCLE: + printk(KERN_INFO PFX "Power cycle is enabled.\n"); + break; + + case IPMI_CHASSIS_POWER_DOWN: + default: + poweroff_control = IPMI_CHASSIS_POWER_DOWN; + break; + } + rv = ipmi_smi_watcher_register(&smi_watcher); - if (rv) + if (rv) { printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); + goto out_err; + } + +#ifdef CONFIG_PROC_FS + file = create_proc_entry("poweroff_control", 0, proc_ipmi_root); + if (!file) { + printk(KERN_ERR PFX "Unable to create proc power control\n"); + } else { + file->nlink = 1; + file->read_proc = proc_read_chassctrl; + file->write_proc = proc_write_chassctrl; + file->owner = THIS_MODULE; + } +#endif + out_err: return rv; } @@ -532,6 +623,10 @@ static __exit void ipmi_poweroff_cleanup(void) { int rv; +#ifdef CONFIG_PROC_FS + remove_proc_entry("poweroff_control", proc_ipmi_root); +#endif + ipmi_smi_watcher_unregister(&smi_watcher); if (ready) { |