X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fchar%2Fipmi%2Fipmi_msghandler.c;h=aa83a0865ec1d75abeddb9066a42aa5ea8ba1444;hb=4dec302ff71ebf48f5784a2d2fc5e3745e6d4d52;hp=d16bd4b5c1174665c5e54e23940e46b5c6e20d12;hpb=177294d19174cf92de22434bb1fc9a8ecdbbe658;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index d16bd4b..aa83a08 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -31,13 +31,12 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include -#include #include #include +#include #include #include #include @@ -48,29 +47,32 @@ #define PFX "IPMI message handler: " -#define IPMI_DRIVER_VERSION "36.0" +#define IPMI_DRIVER_VERSION "39.2" static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); -static int initialized = 0; +static int initialized; #ifdef CONFIG_PROC_FS -struct proc_dir_entry *proc_ipmi_root = NULL; +static struct proc_dir_entry *proc_ipmi_root; #endif /* CONFIG_PROC_FS */ +/* Remain in auto-maintenance mode for this amount of time (in ms). */ +#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 + #define MAX_EVENTS_IN_QUEUE 25 -/* Don't let a message sit in a queue forever, always time it with at lest - the max message timer. This is in milliseconds. */ +/* + * Don't let a message sit in a queue forever, always time it with at lest + * the max message timer. This is in milliseconds. + */ #define MAX_MSG_TIMEOUT 60000 - /* * The main "user" data structure. */ -struct ipmi_user -{ +struct ipmi_user { struct list_head link; /* Set to "0" when the user is destroyed. */ @@ -89,13 +91,13 @@ struct ipmi_user int gets_events; }; -struct cmd_rcvr -{ +struct cmd_rcvr { struct list_head link; ipmi_user_t user; unsigned char netfn; unsigned char cmd; + unsigned int chans; /* * This is used to form a linked lised during mass deletion. @@ -103,12 +105,12 @@ struct cmd_rcvr * or change any data until the RCU period completes. So we * use this next variable during mass deletion so we can have * a list and don't have to wait and restart the search on - * every individual deletion of a command. */ + * every individual deletion of a command. + */ struct cmd_rcvr *next; }; -struct seq_table -{ +struct seq_table { unsigned int inuse : 1; unsigned int broadcast : 1; @@ -116,123 +118,292 @@ struct seq_table unsigned long orig_timeout; unsigned int retries_left; - /* To verify on an incoming send message response that this is - the message that the response is for, we keep a sequence id - and increment it every time we send a message. */ + /* + * To verify on an incoming send message response that this is + * the message that the response is for, we keep a sequence id + * and increment it every time we send a message. + */ long seqid; - /* This is held so we can properly respond to the message on a - timeout, and it is used to hold the temporary data for - retransmission, too. */ + /* + * This is held so we can properly respond to the message on a + * timeout, and it is used to hold the temporary data for + * retransmission, too. + */ struct ipmi_recv_msg *recv_msg; }; -/* Store the information in a msgid (long) to allow us to find a - sequence table entry from the msgid. */ +/* + * Store the information in a msgid (long) to allow us to find a + * sequence table entry from the msgid. + */ #define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff)) #define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ do { \ seq = ((msgid >> 26) & 0x3f); \ seqid = (msgid & 0x3fffff); \ - } while (0) + } while (0) #define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff) -struct ipmi_channel -{ +struct ipmi_channel { unsigned char medium; unsigned char protocol; - /* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, - but may be changed by the user. */ + /* + * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, + * but may be changed by the user. + */ unsigned char address; - /* My LUN. This should generally stay the SMS LUN, but just in - case... */ + /* + * My LUN. This should generally stay the SMS LUN, but just in + * case... + */ unsigned char lun; }; #ifdef CONFIG_PROC_FS -struct ipmi_proc_entry -{ +struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; #endif +struct bmc_device { + struct platform_device *dev; + struct ipmi_device_id id; + unsigned char guid[16]; + int guid_set; + + struct kref refcount; + + /* bmc device attributes */ + struct device_attribute device_id_attr; + struct device_attribute provides_dev_sdrs_attr; + struct device_attribute revision_attr; + struct device_attribute firmware_rev_attr; + struct device_attribute version_attr; + struct device_attribute add_dev_support_attr; + struct device_attribute manufacturer_id_attr; + struct device_attribute product_id_attr; + struct device_attribute guid_attr; + struct device_attribute aux_firmware_rev_attr; +}; + +/* + * Various statistics for IPMI, these index stats[] in the ipmi_smi + * structure. + */ +enum ipmi_stat_indexes { + /* Commands we got from the user that were invalid. */ + IPMI_STAT_sent_invalid_commands = 0, + + /* Commands we sent to the MC. */ + IPMI_STAT_sent_local_commands, + + /* Responses from the MC that were delivered to a user. */ + IPMI_STAT_handled_local_responses, + + /* Responses from the MC that were not delivered to a user. */ + IPMI_STAT_unhandled_local_responses, + + /* Commands we sent out to the IPMB bus. */ + IPMI_STAT_sent_ipmb_commands, + + /* Commands sent on the IPMB that had errors on the SEND CMD */ + IPMI_STAT_sent_ipmb_command_errs, + + /* Each retransmit increments this count. */ + IPMI_STAT_retransmitted_ipmb_commands, + + /* + * When a message times out (runs out of retransmits) this is + * incremented. + */ + IPMI_STAT_timed_out_ipmb_commands, + + /* + * This is like above, but for broadcasts. Broadcasts are + * *not* included in the above count (they are expected to + * time out). + */ + IPMI_STAT_timed_out_ipmb_broadcasts, + + /* Responses I have sent to the IPMB bus. */ + IPMI_STAT_sent_ipmb_responses, + + /* The response was delivered to the user. */ + IPMI_STAT_handled_ipmb_responses, + + /* The response had invalid data in it. */ + IPMI_STAT_invalid_ipmb_responses, + + /* The response didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_ipmb_responses, + + /* Commands we sent out to the IPMB bus. */ + IPMI_STAT_sent_lan_commands, + + /* Commands sent on the IPMB that had errors on the SEND CMD */ + IPMI_STAT_sent_lan_command_errs, + + /* Each retransmit increments this count. */ + IPMI_STAT_retransmitted_lan_commands, + + /* + * When a message times out (runs out of retransmits) this is + * incremented. + */ + IPMI_STAT_timed_out_lan_commands, + + /* Responses I have sent to the IPMB bus. */ + IPMI_STAT_sent_lan_responses, + + /* The response was delivered to the user. */ + IPMI_STAT_handled_lan_responses, + + /* The response had invalid data in it. */ + IPMI_STAT_invalid_lan_responses, + + /* The response didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_lan_responses, + + /* The command was delivered to the user. */ + IPMI_STAT_handled_commands, + + /* The command had invalid data in it. */ + IPMI_STAT_invalid_commands, + + /* The command didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_commands, + + /* Invalid data in an event. */ + IPMI_STAT_invalid_events, + + /* Events that were received with the proper format. */ + IPMI_STAT_events, + + /* Retransmissions on IPMB that failed. */ + IPMI_STAT_dropped_rexmit_ipmb_commands, + + /* Retransmissions on LAN that failed. */ + IPMI_STAT_dropped_rexmit_lan_commands, + + /* This *must* remain last, add new values above this. */ + IPMI_NUM_STATS +}; + + #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 16 -struct ipmi_smi -{ +struct ipmi_smi { /* What interface number are we? */ int intf_num; struct kref refcount; - /* The list of upper layers that are using me. seq_lock - * protects this. */ + /* Used for a list of interfaces. */ + struct list_head link; + + /* + * The list of upper layers that are using me. seq_lock + * protects this. + */ struct list_head users; + /* Information to supply to users. */ + unsigned char ipmi_version_major; + unsigned char ipmi_version_minor; + /* Used for wake ups at startup. */ wait_queue_head_t waitq; - /* The IPMI version of the BMC on the other end. */ - unsigned char version_major; - unsigned char version_minor; + struct bmc_device *bmc; + char *my_dev_name; + char *sysfs_name; - /* This is the lower-layer's sender routine. */ + /* + * This is the lower-layer's sender routine. Note that you + * must either be holding the ipmi_interfaces_mutex or be in + * an umpreemptible region to use this. You must fetch the + * value into a local variable and make sure it is not NULL. + */ 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; + /* A list of proc entries for this interface. */ + struct mutex 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 - interface to match them up with their responses. A routine - is called periodically to time the items in this list. */ + /* Driver-model device for the system interface. */ + struct device *si_dev; + + /* + * A table of sequence numbers for this interface. We use the + * sequence numbers for IPMB messages that go out of the + * interface to match them up with their responses. A routine + * is called periodically to time the items in this list. + */ spinlock_t seq_lock; struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; int curr_seq; - /* Messages that were delayed for some reason (out of memory, - for instance), will go in here to be processed later in a - periodic timer interrupt. */ + /* + * Messages that were delayed for some reason (out of memory, + * for instance), will go in here to be processed later in a + * periodic timer interrupt. + */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; - /* The list of command receivers that are registered for commands - on this interface. */ - struct semaphore cmd_rcvrs_lock; + /* + * The list of command receivers that are registered for commands + * on this interface. + */ + struct mutex cmd_rcvrs_mutex; struct list_head cmd_rcvrs; - /* Events that were queues because no one was there to receive - them. */ + /* + * Events that were queues because no one was there to receive + * them. + */ spinlock_t events_lock; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ + char delivering_events; + char event_msg_printed; - /* The event receiver for my BMC, only really used at panic - shutdown as a place to store this. */ + /* + * The event receiver for my BMC, only really used at panic + * shutdown as a place to store this. + */ unsigned char event_receiver; unsigned char event_receiver_lun; unsigned char local_sel_device; unsigned char local_event_generator; - /* A cheap hack, if this is non-null and a message to an - interface comes in with a NULL user, call this routine with - it. Note that the message will still be freed by the - caller. This only works on the system interface. */ + /* For handling of maintenance mode. */ + int maintenance_mode; + int maintenance_mode_enable; + int auto_maintenance_timeout; + spinlock_t maintenance_mode_lock; /* Used in a timer... */ + + /* + * A cheap hack, if this is non-null and a message to an + * interface comes in with a NULL user, call this routine with + * it. Note that the message will still be freed by the + * caller. This only works on the system interface. + */ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); - /* When we are scanning the channels for an SMI, this will - tell which channel we are scanning. */ + /* + * When we are scanning the channels for an SMI, this will + * tell which channel we are scanning. + */ int curr_channel; /* Channel information */ @@ -242,94 +413,57 @@ struct ipmi_smi struct proc_dir_entry *proc_dir; char proc_dir_name[10]; - spinlock_t counter_lock; /* For making counters atomic. */ - - /* Commands we got that were invalid. */ - unsigned int sent_invalid_commands; - - /* Commands we sent to the MC. */ - unsigned int sent_local_commands; - /* Responses from the MC that were delivered to a user. */ - unsigned int handled_local_responses; - /* Responses from the MC that were not delivered to a user. */ - unsigned int unhandled_local_responses; - - /* Commands we sent out to the IPMB bus. */ - unsigned int sent_ipmb_commands; - /* Commands sent on the IPMB that had errors on the SEND CMD */ - unsigned int sent_ipmb_command_errs; - /* Each retransmit increments this count. */ - unsigned int retransmitted_ipmb_commands; - /* When a message times out (runs out of retransmits) this is - incremented. */ - unsigned int timed_out_ipmb_commands; - - /* This is like above, but for broadcasts. Broadcasts are - *not* included in the above count (they are expected to - time out). */ - unsigned int timed_out_ipmb_broadcasts; - - /* Responses I have sent to the IPMB bus. */ - unsigned int sent_ipmb_responses; - - /* The response was delivered to the user. */ - unsigned int handled_ipmb_responses; - /* The response had invalid data in it. */ - unsigned int invalid_ipmb_responses; - /* The response didn't have anyone waiting for it. */ - unsigned int unhandled_ipmb_responses; - - /* Commands we sent out to the IPMB bus. */ - unsigned int sent_lan_commands; - /* Commands sent on the IPMB that had errors on the SEND CMD */ - unsigned int sent_lan_command_errs; - /* Each retransmit increments this count. */ - unsigned int retransmitted_lan_commands; - /* When a message times out (runs out of retransmits) this is - incremented. */ - unsigned int timed_out_lan_commands; + atomic_t stats[IPMI_NUM_STATS]; - /* Responses I have sent to the IPMB bus. */ - unsigned int sent_lan_responses; + /* + * run_to_completion duplicate of smb_info, smi_info + * and ipmi_serial_info structures. Used to decrease numbers of + * parameters passed by "low" level IPMI code. + */ + int run_to_completion; +}; +#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) - /* The response was delivered to the user. */ - unsigned int handled_lan_responses; - /* The response had invalid data in it. */ - unsigned int invalid_lan_responses; - /* The response didn't have anyone waiting for it. */ - unsigned int unhandled_lan_responses; +/** + * The driver model view of the IPMI messaging driver. + */ +static struct platform_driver ipmidriver = { + .driver = { + .name = "ipmi", + .bus = &platform_bus_type + } +}; +static DEFINE_MUTEX(ipmidriver_mutex); - /* The command was delivered to the user. */ - unsigned int handled_commands; - /* The command had invalid data in it. */ - unsigned int invalid_commands; - /* The command didn't have anyone waiting for it. */ - unsigned int unhandled_commands; +static LIST_HEAD(ipmi_interfaces); +static DEFINE_MUTEX(ipmi_interfaces_mutex); - /* Invalid data in an event. */ - unsigned int invalid_events; - /* Events that were received with the proper format. */ - unsigned int events; -}; +/* + * List of watchers that want to know when smi's are added and deleted. + */ +static LIST_HEAD(smi_watchers); +static DEFINE_MUTEX(smi_watchers_mutex); -/* Used to mark an interface entry that cannot be used but is not a - * free entry, either, primarily used at creation and deletion time so - * a slot doesn't get reused too quickly. */ -#define IPMI_INVALID_INTERFACE_ENTRY ((ipmi_smi_t) ((long) 1)) -#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \ - || (i == IPMI_INVALID_INTERFACE_ENTRY)) -#define MAX_IPMI_INTERFACES 4 -static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES]; +#define ipmi_inc_stat(intf, stat) \ + atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) +#define ipmi_get_stat(intf, stat) \ + ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) -/* Directly protects the ipmi_interfaces data structure. */ -static DEFINE_SPINLOCK(interfaces_lock); +static int is_lan_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_LAN_ADDR_TYPE; +} -/* List of watchers that want to know when smi's are added and - deleted. */ -static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers); -static DECLARE_RWSEM(smi_watchers_sem); +static int is_ipmb_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_ADDR_TYPE; +} +static int is_ipmb_bcast_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE; +} static void free_recv_msg_list(struct list_head *q) { @@ -341,32 +475,41 @@ static void free_recv_msg_list(struct list_head *q) } } +static void free_smi_msg_list(struct list_head *q) +{ + struct ipmi_smi_msg *msg, *msg2; + + list_for_each_entry_safe(msg, msg2, q, link) { + list_del(&msg->link); + ipmi_free_smi_msg(msg); + } +} + static void clean_up_interface_data(ipmi_smi_t intf) { int i; struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; - free_recv_msg_list(&intf->waiting_msgs); + free_smi_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); - /* Wholesale remove all the entries from the list in the - * interface and wait for RCU to know that none are in use. */ - down(&intf->cmd_rcvrs_lock); - list_add_rcu(&list, &intf->cmd_rcvrs); - list_del_rcu(&intf->cmd_rcvrs); - up(&intf->cmd_rcvrs_lock); - synchronize_rcu(); + /* + * Wholesale remove all the entries from the list in the + * interface and wait for RCU to know that none are in use. + */ + mutex_lock(&intf->cmd_rcvrs_mutex); + INIT_LIST_HEAD(&list); + list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); + mutex_unlock(&intf->cmd_rcvrs_mutex); list_for_each_entry_safe(rcvr, rcvr2, &list, link) kfree(rcvr); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if ((intf->seq_table[i].inuse) - && (intf->seq_table[i].recv_msg)) - { + && (intf->seq_table[i].recv_msg)) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); - } } } @@ -378,48 +521,86 @@ static void intf_free(struct kref *ref) kfree(intf); } +struct watcher_entry { + int intf_num; + ipmi_smi_t intf; + struct list_head link; +}; + int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { - int i; - unsigned long flags; + ipmi_smi_t intf; + LIST_HEAD(to_deliver); + struct watcher_entry *e, *e2; + + mutex_lock(&smi_watchers_mutex); - down_write(&smi_watchers_sem); - list_add(&(watcher->link), &smi_watchers); - up_write(&smi_watchers_sem); - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - ipmi_smi_t intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + mutex_lock(&ipmi_interfaces_mutex); + + /* Build a list of things to deliver. */ + list_for_each_entry(intf, &ipmi_interfaces, link) { + if (intf->intf_num == -1) continue; - spin_unlock_irqrestore(&interfaces_lock, flags); - watcher->new_smi(i); - spin_lock_irqsave(&interfaces_lock, flags); + e = kmalloc(sizeof(*e), GFP_KERNEL); + if (!e) + goto out_err; + kref_get(&intf->refcount); + e->intf = intf; + e->intf_num = intf->intf_num; + list_add_tail(&e->link, &to_deliver); + } + + /* We will succeed, so add it to the list. */ + list_add(&watcher->link, &smi_watchers); + + mutex_unlock(&ipmi_interfaces_mutex); + + list_for_each_entry_safe(e, e2, &to_deliver, link) { + list_del(&e->link); + watcher->new_smi(e->intf_num, e->intf->si_dev); + kref_put(&e->intf->refcount, intf_free); + kfree(e); } - spin_unlock_irqrestore(&interfaces_lock, flags); + + mutex_unlock(&smi_watchers_mutex); + return 0; + + out_err: + mutex_unlock(&ipmi_interfaces_mutex); + mutex_unlock(&smi_watchers_mutex); + list_for_each_entry_safe(e, e2, &to_deliver, link) { + list_del(&e->link); + kref_put(&e->intf->refcount, intf_free); + kfree(e); + } + return -ENOMEM; } +EXPORT_SYMBOL(ipmi_smi_watcher_register); int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) { - down_write(&smi_watchers_sem); + mutex_lock(&smi_watchers_mutex); list_del(&(watcher->link)); - up_write(&smi_watchers_sem); + mutex_unlock(&smi_watchers_mutex); return 0; } +EXPORT_SYMBOL(ipmi_smi_watcher_unregister); +/* + * Must be called with smi_watchers_mutex held. + */ static void -call_smi_watchers(int i) +call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; - down_read(&smi_watchers_sem); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { - w->new_smi(i); + w->new_smi(i, dev); module_put(w->owner); } } - up_read(&smi_watchers_sem); } static int @@ -439,9 +620,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) return (smi_addr1->lun == smi_addr2->lun); } - if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) { struct ipmi_ipmb_addr *ipmb_addr1 = (struct ipmi_ipmb_addr *) addr1; struct ipmi_ipmb_addr *ipmb_addr2 @@ -451,7 +630,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) && (ipmb_addr1->lun == ipmb_addr2->lun)); } - if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) { + if (is_lan_addr(addr1)) { struct ipmi_lan_addr *lan_addr1 = (struct ipmi_lan_addr *) addr1; struct ipmi_lan_addr *lan_addr2 @@ -469,9 +648,8 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) int ipmi_validate_addr(struct ipmi_addr *addr, int len) { - if (len < sizeof(struct ipmi_system_interface_addr)) { + if (len < sizeof(struct ipmi_system_interface_addr)) return -EINVAL; - } if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if (addr->channel != IPMI_BMC_CHANNEL) @@ -480,28 +658,25 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len) } if ((addr->channel == IPMI_BMC_CHANNEL) - || (addr->channel >= IPMI_NUM_CHANNELS) + || (addr->channel >= IPMI_MAX_CHANNELS) || (addr->channel < 0)) return -EINVAL; - if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { - if (len < sizeof(struct ipmi_ipmb_addr)) { + if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { + if (len < sizeof(struct ipmi_ipmb_addr)) return -EINVAL; - } return 0; } - if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { - if (len < sizeof(struct ipmi_lan_addr)) { + if (is_lan_addr(addr)) { + if (len < sizeof(struct ipmi_lan_addr)) return -EINVAL; - } return 0; } return -EINVAL; } +EXPORT_SYMBOL(ipmi_validate_addr); unsigned int ipmi_addr_length(int addr_type) { @@ -509,34 +684,28 @@ unsigned int ipmi_addr_length(int addr_type) return sizeof(struct ipmi_system_interface_addr); if ((addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) return sizeof(struct ipmi_ipmb_addr); - } if (addr_type == IPMI_LAN_ADDR_TYPE) return sizeof(struct ipmi_lan_addr); return 0; } +EXPORT_SYMBOL(ipmi_addr_length); static void deliver_response(struct ipmi_recv_msg *msg) { - if (! msg->user) { + if (!msg->user) { ipmi_smi_t intf = msg->user_msg_data; - unsigned long flags; /* Special handling for NULL users. */ if (intf->null_user_handler) { intf->null_user_handler(intf, msg); - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_local_responses); } else { /* No handler, so give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_local_responses); } ipmi_free_recv_msg(msg); } else { @@ -545,9 +714,22 @@ static void deliver_response(struct ipmi_recv_msg *msg) } } -/* Find the next sequence number not being used and add the given - message with the given timeout to the sequence table. This must be - called with the interface's seq_lock held. */ +static void +deliver_err_response(struct ipmi_recv_msg *msg, int err) +{ + msg->recv_type = IPMI_RESPONSE_RECV_TYPE; + msg->msg_data[0] = err; + msg->msg.netfn |= 1; /* Convert to a response. */ + msg->msg.data_len = 1; + msg->msg.data = msg->msg_data; + deliver_response(msg); +} + +/* + * Find the next sequence number not being used and add the given + * message with the given timeout to the sequence table. This must be + * called with the interface's seq_lock held. + */ static int intf_next_seq(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, unsigned long timeout, @@ -559,19 +741,19 @@ static int intf_next_seq(ipmi_smi_t intf, int rv = 0; unsigned int i; - for (i = intf->curr_seq; - (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; - i = (i+1)%IPMI_IPMB_NUM_SEQ) - { - if (! intf->seq_table[i].inuse) + for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; + i = (i+1)%IPMI_IPMB_NUM_SEQ) { + if (!intf->seq_table[i].inuse) break; } - if (! intf->seq_table[i].inuse) { + if (!intf->seq_table[i].inuse) { intf->seq_table[i].recv_msg = recv_msg; - /* Start with the maximum timeout, when the send response - comes in we will start the real timer. */ + /* + * Start with the maximum timeout, when the send response + * comes in we will start the real timer. + */ intf->seq_table[i].timeout = MAX_MSG_TIMEOUT; intf->seq_table[i].orig_timeout = timeout; intf->seq_table[i].retries_left = retries; @@ -584,15 +766,17 @@ static int intf_next_seq(ipmi_smi_t intf, } else { rv = -EAGAIN; } - + return rv; } -/* Return the receive message for the given sequence number and - release the sequence number so it can be reused. Some other data - is passed in to be sure the message matches up correctly (to help - guard against message coming in after their timeout and the - sequence number being reused). */ +/* + * Return the receive message for the given sequence number and + * release the sequence number so it can be reused. Some other data + * is passed in to be sure the message matches up correctly (to help + * guard against message coming in after their timeout and the + * sequence number being reused). + */ static int intf_find_seq(ipmi_smi_t intf, unsigned char seq, short channel, @@ -611,11 +795,9 @@ static int intf_find_seq(ipmi_smi_t intf, if (intf->seq_table[seq].inuse) { struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; - if ((msg->addr.channel == channel) - && (msg->msg.cmd == cmd) - && (msg->msg.netfn == netfn) - && (ipmi_addr_equal(addr, &(msg->addr)))) - { + if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) + && (msg->msg.netfn == netfn) + && (ipmi_addr_equal(addr, &(msg->addr)))) { *recv_msg = msg; intf->seq_table[seq].inuse = 0; rv = 0; @@ -640,11 +822,12 @@ static int intf_start_seq_timer(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); - /* We do this verification because the user can be deleted - while a message is outstanding. */ + /* + * We do this verification because the user can be deleted + * while a message is outstanding. + */ if ((intf->seq_table[seq].inuse) - && (intf->seq_table[seq].seqid == seqid)) - { + && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->timeout = ent->orig_timeout; rv = 0; @@ -669,11 +852,12 @@ static int intf_err_seq(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); - /* We do this verification because the user can be deleted - while a message is outstanding. */ + /* + * We do this verification because the user can be deleted + * while a message is outstanding. + */ if ((intf->seq_table[seq].inuse) - && (intf->seq_table[seq].seqid == seqid)) - { + && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->inuse = 0; @@ -682,14 +866,8 @@ static int intf_err_seq(ipmi_smi_t intf, } spin_unlock_irqrestore(&(intf->seq_lock), flags); - if (msg) { - msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - msg->msg_data[0] = err; - msg->msg.netfn |= 1; /* Convert to a response. */ - msg->msg.data_len = 1; - msg->msg.data = msg->msg_data; - deliver_response(msg); - } + if (msg) + deliver_err_response(msg, err); return rv; } @@ -705,42 +883,50 @@ int ipmi_create_user(unsigned int if_num, int rv = 0; ipmi_smi_t intf; - /* There is no module usecount here, because it's not - required. Since this can only be used by and called from - other modules, they will implicitly use this module, and - thus this can't be removed unless the other modules are - removed. */ + /* + * There is no module usecount here, because it's not + * required. Since this can only be used by and called from + * other modules, they will implicitly use this module, and + * thus this can't be removed unless the other modules are + * removed. + */ if (handler == NULL) return -EINVAL; - /* Make sure the driver is actually initialized, this handles - problems with initialization order. */ + /* + * Make sure the driver is actually initialized, this handles + * problems with initialization order. + */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; - /* The init code doesn't return an error if it was turned - off, but it won't initialize. Check that. */ + /* + * The init code doesn't return an error if it was turned + * off, but it won't initialize. Check that. + */ if (!initialized) return -ENODEV; } new_user = kmalloc(sizeof(*new_user), GFP_KERNEL); - if (! new_user) + if (!new_user) return -ENOMEM; - spin_lock_irqsave(&interfaces_lock, flags); - intf = ipmi_interfaces[if_num]; - if ((if_num >= MAX_IPMI_INTERFACES) || IPMI_INVALID_INTERFACE(intf)) { - spin_unlock_irqrestore(&interfaces_lock, flags); - return -EINVAL; + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) + goto found; } + /* Not found, return an error */ + rv = -EINVAL; + goto out_kfree; + found: /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); - spin_unlock_irqrestore(&interfaces_lock, flags); kref_init(&new_user->refcount); new_user->handler = handler; @@ -750,17 +936,23 @@ int ipmi_create_user(unsigned int if_num, if (!try_module_get(intf->handlers->owner)) { rv = -ENODEV; - goto out_err; + goto out_kref; } if (intf->handlers->inc_usecount) { rv = intf->handlers->inc_usecount(intf->send_info); if (rv) { module_put(intf->handlers->owner); - goto out_err; + goto out_kref; } } + /* + * Hold the lock so intf->handlers is guaranteed to be good + * until now + */ + mutex_unlock(&ipmi_interfaces_mutex); + new_user->valid = 1; spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); @@ -768,11 +960,14 @@ int ipmi_create_user(unsigned int if_num, *user = new_user; return 0; - out_err: - kfree(new_user); +out_kref: kref_put(&intf->refcount, intf_free); +out_kfree: + mutex_unlock(&ipmi_interfaces_mutex); + kfree(new_user); return rv; } +EXPORT_SYMBOL(ipmi_create_user); static void free_user(struct kref *ref) { @@ -782,15 +977,13 @@ static void free_user(struct kref *ref) int ipmi_destroy_user(ipmi_user_t user) { - int rv = -ENODEV; ipmi_smi_t intf = user->intf; int i; unsigned long flags; struct cmd_rcvr *rcvr; - struct list_head *entry1, *entry2; struct cmd_rcvr *rcvrs = NULL; - user->valid = 1; + user->valid = 0; /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); @@ -798,9 +991,9 @@ int ipmi_destroy_user(ipmi_user_t user) for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if (intf->seq_table[i].inuse - && (intf->seq_table[i].recv_msg->user == user)) - { + && (intf->seq_table[i].recv_msg->user == user)) { intf->seq_table[i].inuse = 0; + ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } spin_unlock_irqrestore(&intf->seq_lock, flags); @@ -811,16 +1004,15 @@ int ipmi_destroy_user(ipmi_user_t user) * since other things may be using it till we do * synchronize_rcu()) then free everything in that list. */ - down(&intf->cmd_rcvrs_lock); - list_for_each_safe_rcu(entry1, entry2, &intf->cmd_rcvrs) { - rcvr = list_entry(entry1, struct cmd_rcvr, link); + mutex_lock(&intf->cmd_rcvrs_mutex); + list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if (rcvr->user == user) { list_del_rcu(&rcvr->link); rcvr->next = rcvrs; rcvrs = rcvr; } } - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; @@ -828,24 +1020,30 @@ int ipmi_destroy_user(ipmi_user_t user) kfree(rcvr); } - module_put(intf->handlers->owner); - if (intf->handlers->dec_usecount) - intf->handlers->dec_usecount(intf->send_info); + mutex_lock(&ipmi_interfaces_mutex); + if (intf->handlers) { + module_put(intf->handlers->owner); + if (intf->handlers->dec_usecount) + intf->handlers->dec_usecount(intf->send_info); + } + mutex_unlock(&ipmi_interfaces_mutex); kref_put(&intf->refcount, intf_free); kref_put(&user->refcount, free_user); - return rv; + return 0; } +EXPORT_SYMBOL(ipmi_destroy_user); void ipmi_get_version(ipmi_user_t user, unsigned char *major, unsigned char *minor) { - *major = user->intf->version_major; - *minor = user->intf->version_minor; + *major = user->intf->ipmi_version_major; + *minor = user->intf->ipmi_version_minor; } +EXPORT_SYMBOL(ipmi_get_version); int ipmi_set_my_address(ipmi_user_t user, unsigned int channel, @@ -856,6 +1054,7 @@ int ipmi_set_my_address(ipmi_user_t user, user->intf->channels[channel].address = address; return 0; } +EXPORT_SYMBOL(ipmi_set_my_address); int ipmi_get_my_address(ipmi_user_t user, unsigned int channel, @@ -866,6 +1065,7 @@ int ipmi_get_my_address(ipmi_user_t user, *address = user->intf->channels[channel].address; return 0; } +EXPORT_SYMBOL(ipmi_get_my_address); int ipmi_set_my_LUN(ipmi_user_t user, unsigned int channel, @@ -876,6 +1076,7 @@ int ipmi_set_my_LUN(ipmi_user_t user, user->intf->channels[channel].lun = LUN & 0x3; return 0; } +EXPORT_SYMBOL(ipmi_set_my_LUN); int ipmi_get_my_LUN(ipmi_user_t user, unsigned int channel, @@ -886,73 +1087,168 @@ int ipmi_get_my_LUN(ipmi_user_t user, *address = user->intf->channels[channel].lun; return 0; } +EXPORT_SYMBOL(ipmi_get_my_LUN); -int ipmi_set_gets_events(ipmi_user_t user, int val) +int ipmi_get_maintenance_mode(ipmi_user_t user) { - unsigned long flags; - ipmi_smi_t intf = user->intf; - struct ipmi_recv_msg *msg, *msg2; - struct list_head msgs; + int mode; + unsigned long flags; - INIT_LIST_HEAD(&msgs); + spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); + mode = user->intf->maintenance_mode; + spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); - spin_lock_irqsave(&intf->events_lock, flags); - user->gets_events = val; + return mode; +} +EXPORT_SYMBOL(ipmi_get_maintenance_mode); - if (val) { - /* Deliver any queued events. */ - list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) { - list_del(&msg->link); - list_add_tail(&msg->link, &msgs); - } - } +static void maintenance_mode_update(ipmi_smi_t intf) +{ + if (intf->handlers->set_maintenance_mode) + intf->handlers->set_maintenance_mode( + intf->send_info, intf->maintenance_mode_enable); +} - /* Hold the events lock while doing this to preserve order. */ - list_for_each_entry_safe(msg, msg2, &msgs, link) { - msg->user = user; - kref_get(&user->refcount); - deliver_response(msg); +int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) +{ + int rv = 0; + unsigned long flags; + ipmi_smi_t intf = user->intf; + + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + if (intf->maintenance_mode != mode) { + switch (mode) { + case IPMI_MAINTENANCE_MODE_AUTO: + intf->maintenance_mode = mode; + intf->maintenance_mode_enable + = (intf->auto_maintenance_timeout > 0); + break; + + case IPMI_MAINTENANCE_MODE_OFF: + intf->maintenance_mode = mode; + intf->maintenance_mode_enable = 0; + break; + + case IPMI_MAINTENANCE_MODE_ON: + intf->maintenance_mode = mode; + intf->maintenance_mode_enable = 1; + break; + + default: + rv = -EINVAL; + goto out_unlock; + } + + maintenance_mode_update(intf); + } + out_unlock: + spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); + + return rv; +} +EXPORT_SYMBOL(ipmi_set_maintenance_mode); + +int ipmi_set_gets_events(ipmi_user_t user, int val) +{ + unsigned long flags; + ipmi_smi_t intf = user->intf; + struct ipmi_recv_msg *msg, *msg2; + struct list_head msgs; + + INIT_LIST_HEAD(&msgs); + + spin_lock_irqsave(&intf->events_lock, flags); + user->gets_events = val; + + if (intf->delivering_events) + /* + * Another thread is delivering events for this, so + * let it handle any new events. + */ + goto out; + + /* Deliver any queued events. */ + while (user->gets_events && !list_empty(&intf->waiting_events)) { + list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) + list_move_tail(&msg->link, &msgs); + intf->waiting_events_count = 0; + if (intf->event_msg_printed) { + printk(KERN_WARNING PFX "Event queue no longer" + " full\n"); + intf->event_msg_printed = 0; + } + + intf->delivering_events = 1; + spin_unlock_irqrestore(&intf->events_lock, flags); + + list_for_each_entry_safe(msg, msg2, &msgs, link) { + msg->user = user; + kref_get(&user->refcount); + deliver_response(msg); + } + + spin_lock_irqsave(&intf->events_lock, flags); + intf->delivering_events = 0; } + out: spin_unlock_irqrestore(&intf->events_lock, flags); return 0; } +EXPORT_SYMBOL(ipmi_set_gets_events); static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned char chan) { struct cmd_rcvr *rcvr; list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { - if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & (1 << chan))) return rcvr; } return NULL; } +static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, + unsigned char netfn, + unsigned char cmd, + unsigned int chans) +{ + struct cmd_rcvr *rcvr; + + list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & chans)) + return 0; + } + return 1; +} + int ipmi_register_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; - struct cmd_rcvr *entry; int rv = 0; rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); - if (! rcvr) + if (!rcvr) return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; + rcvr->chans = chans; rcvr->user = user; - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ - entry = find_cmd_rcvr(intf, netfn, cmd); - if (entry) { + if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { rv = -EBUSY; goto out_unlock; } @@ -960,46 +1256,57 @@ int ipmi_register_for_cmd(ipmi_user_t user, list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); return rv; } +EXPORT_SYMBOL(ipmi_register_for_cmd); int ipmi_unregister_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; + struct cmd_rcvr *rcvrs = NULL; + int i, rv = -ENOENT; - down(&intf->cmd_rcvrs_lock); - /* Make sure the command/netfn is not already registered. */ - rcvr = find_cmd_rcvr(intf, netfn, cmd); - if ((rcvr) && (rcvr->user == user)) { - list_del_rcu(&rcvr->link); - up(&intf->cmd_rcvrs_lock); - synchronize_rcu(); + mutex_lock(&intf->cmd_rcvrs_mutex); + for (i = 0; i < IPMI_NUM_CHANNELS; i++) { + if (((1 << i) & chans) == 0) + continue; + rcvr = find_cmd_rcvr(intf, netfn, cmd, i); + if (rcvr == NULL) + continue; + if (rcvr->user == user) { + rv = 0; + rcvr->chans &= ~chans; + if (rcvr->chans == 0) { + list_del_rcu(&rcvr->link); + rcvr->next = rcvrs; + rcvrs = rcvr; + } + } + } + mutex_unlock(&intf->cmd_rcvrs_mutex); + synchronize_rcu(); + while (rcvrs) { + rcvr = rcvrs; + rcvrs = rcvr->next; kfree(rcvr); - return 0; - } else { - up(&intf->cmd_rcvrs_lock); - return -ENOENT; } + return rv; } - -void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) -{ - ipmi_smi_t intf = user->intf; - intf->handlers->set_run_to_completion(intf->send_info, val); -} +EXPORT_SYMBOL(ipmi_unregister_for_cmd); static unsigned char ipmb_checksum(unsigned char *data, int size) { unsigned char csum = 0; - + for (; size > 0; size--, data++) csum += *data; @@ -1041,8 +1348,10 @@ static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, = ipmb_checksum(&(smi_msg->data[i+6]), smi_msg->data_size-6); - /* Add on the checksum size and the offset from the - broadcast. */ + /* + * Add on the checksum size and the offset from the + * broadcast. + */ smi_msg->data_size += 1 + i; smi_msg->msgid = msgid; @@ -1078,17 +1387,21 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, = ipmb_checksum(&(smi_msg->data[7]), smi_msg->data_size-7); - /* Add on the checksum size and the offset from the - broadcast. */ + /* + * Add on the checksum size and the offset from the + * broadcast. + */ smi_msg->data_size += 1; smi_msg->msgid = msgid; } -/* Separate from ipmi_request so that the user does not have to be - supplied in certain circumstances (mainly at panic time). If - messages are supplied, they will be freed, even if an error - occurs. */ +/* + * Separate from ipmi_request so that the user does not have to be + * supplied in certain circumstances (mainly at panic time). If + * messages are supplied, they will be freed, even if an error + * occurs. + */ static int i_ipmi_request(ipmi_user_t user, ipmi_smi_t intf, struct ipmi_addr *addr, @@ -1103,25 +1416,25 @@ static int i_ipmi_request(ipmi_user_t user, int retries, unsigned int retry_time_ms) { - int rv = 0; - struct ipmi_smi_msg *smi_msg; - struct ipmi_recv_msg *recv_msg; - unsigned long flags; + int rv = 0; + struct ipmi_smi_msg *smi_msg; + struct ipmi_recv_msg *recv_msg; + unsigned long flags; + struct ipmi_smi_handlers *handlers; - if (supplied_recv) { + if (supplied_recv) recv_msg = supplied_recv; - } else { + else { recv_msg = ipmi_alloc_recv_msg(); - if (recv_msg == NULL) { + if (recv_msg == NULL) return -ENOMEM; - } } recv_msg->user_msg_data = user_msg_data; - if (supplied_smi) { + if (supplied_smi) smi_msg = (struct ipmi_smi_msg *) supplied_smi; - } else { + else { smi_msg = ipmi_alloc_smi_msg(); if (smi_msg == NULL) { ipmi_free_recv_msg(recv_msg); @@ -1129,12 +1442,21 @@ static int i_ipmi_request(ipmi_user_t user, } } + rcu_read_lock(); + handlers = intf->handlers; + if (!handlers) { + rv = -ENODEV; + goto out_err; + } + recv_msg->user = user; if (user) kref_get(&user->refcount); recv_msg->msgid = msgid; - /* Store the message to send in the receive message so timeout - responses can get the proper response data. */ + /* + * Store the message to send in the receive message so timeout + * responses can get the proper response data. + */ recv_msg->msg = *msg; if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { @@ -1148,9 +1470,7 @@ static int i_ipmi_request(ipmi_user_t user, smi_addr = (struct ipmi_system_interface_addr *) addr; if (smi_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1160,21 +1480,34 @@ static int i_ipmi_request(ipmi_user_t user, if ((msg->netfn == IPMI_NETFN_APP_REQUEST) && ((msg->cmd == IPMI_SEND_MSG_CMD) || (msg->cmd == IPMI_GET_MSG_CMD) - || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) - { - /* We don't let the user do these, since we manage - the sequence numbers. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { + /* + * We don't let the user do these, since we manage + * the sequence numbers. + */ + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } + if (((msg->netfn == IPMI_NETFN_APP_REQUEST) + && ((msg->cmd == IPMI_COLD_RESET_CMD) + || (msg->cmd == IPMI_WARM_RESET_CMD))) + || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)) { + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + intf->auto_maintenance_timeout + = IPMI_MAINTENANCE_MODE_TIMEOUT; + if (!intf->maintenance_mode + && !intf->maintenance_mode_enable) { + intf->maintenance_mode_enable = 1; + maintenance_mode_update(intf); + } + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); + } + if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } @@ -1186,31 +1519,22 @@ static int i_ipmi_request(ipmi_user_t user, if (msg->data_len > 0) memcpy(&(smi_msg->data[2]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 2; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_local_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); - } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + ipmi_inc_stat(intf, sent_local_commands); + } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { struct ipmi_ipmb_addr *ipmb_addr; unsigned char ipmb_seq; long seqid; int broadcast = 0; if (addr->channel >= IPMI_MAX_CHANNELS) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if (intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_IPMB) - { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + != IPMI_CHANNEL_MEDIUM_IPMB) { + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1222,9 +1546,11 @@ static int i_ipmi_request(ipmi_user_t user, retries = 4; } if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { - /* Broadcasts add a zero at the beginning of the - message, but otherwise is the same as an IPMB - address. */ + /* + * Broadcasts add a zero at the beginning of the + * message, but otherwise is the same as an IPMB + * address. + */ addr->addr_type = IPMI_IPMB_ADDR_TYPE; broadcast = 1; } @@ -1234,21 +1560,19 @@ static int i_ipmi_request(ipmi_user_t user, if (retry_time_ms == 0) retry_time_ms = 1000; - /* 9 for the header and 1 for the checksum, plus - possibly one for the broadcast. */ + /* + * 9 for the header and 1 for the checksum, plus + * possibly one for the broadcast. + */ if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } ipmb_addr = (struct ipmi_ipmb_addr *) addr; if (ipmb_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1256,29 +1580,29 @@ static int i_ipmi_request(ipmi_user_t user, memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); if (recv_msg->msg.netfn & 0x1) { - /* It's a response, so use the user's sequence - from msgid. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_ipmb_responses); format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, msgid, broadcast, source_address, source_lun); - /* Save the receive message so we can use it - to deliver the response. */ + /* + * Save the receive message so we can use it + * to deliver the response. + */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); - spin_lock(&intf->counter_lock); - intf->sent_ipmb_commands++; - spin_unlock(&intf->counter_lock); - - /* Create a sequence number with a 1 second - timeout and 4 retries. */ + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, @@ -1287,57 +1611,62 @@ static int i_ipmi_request(ipmi_user_t user, &ipmb_seq, &seqid); if (rv) { - /* We have used up all the sequence numbers, - probably, so abort. */ + /* + * We have used up all the sequence numbers, + * probably, so abort. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } - /* Store the sequence number in the message, - so that when the send message response - comes back we can start the timer. */ + ipmi_inc_stat(intf, sent_ipmb_commands); + + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ format_ipmb_msg(smi_msg, msg, ipmb_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, broadcast, source_address, source_lun); - /* Copy the message into the recv message data, so we - can retransmit it later if necessary. */ + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; - /* We don't unlock until here, because we need - to copy the completed message into the - recv_msg before we release the lock. - Otherwise, race conditions may bite us. I - know that's pretty paranoid, but I prefer - to be correct. */ + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } - } else if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { + } else if (is_lan_addr(addr)) { struct ipmi_lan_addr *lan_addr; unsigned char ipmb_seq; long seqid; - if (addr->channel >= IPMI_NUM_CHANNELS) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + if (addr->channel >= IPMI_MAX_CHANNELS) { + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if ((intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_8023LAN) + != IPMI_CHANNEL_MEDIUM_8023LAN) && (intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_ASYNC)) - { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + != IPMI_CHANNEL_MEDIUM_ASYNC)) { + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1350,18 +1679,14 @@ static int i_ipmi_request(ipmi_user_t user, /* 11 for the header and 1 for the checksum. */ if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } lan_addr = (struct ipmi_lan_addr *) addr; if (lan_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1369,28 +1694,28 @@ static int i_ipmi_request(ipmi_user_t user, memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); if (recv_msg->msg.netfn & 0x1) { - /* It's a response, so use the user's sequence - from msgid. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_lan_responses); format_lan_msg(smi_msg, msg, lan_addr, msgid, msgid, source_lun); - /* Save the receive message so we can use it - to deliver the response. */ + /* + * Save the receive message so we can use it + * to deliver the response. + */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); - spin_lock(&intf->counter_lock); - intf->sent_lan_commands++; - spin_unlock(&intf->counter_lock); - - /* Create a sequence number with a 1 second - timeout and 4 retries. */ + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, @@ -1399,40 +1724,48 @@ static int i_ipmi_request(ipmi_user_t user, &ipmb_seq, &seqid); if (rv) { - /* We have used up all the sequence numbers, - probably, so abort. */ + /* + * We have used up all the sequence numbers, + * probably, so abort. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } - /* Store the sequence number in the message, - so that when the send message response - comes back we can start the timer. */ + ipmi_inc_stat(intf, sent_lan_commands); + + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ format_lan_msg(smi_msg, msg, lan_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, source_lun); - /* Copy the message into the recv message data, so we - can retransmit it later if necessary. */ + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; - /* We don't unlock until here, because we need - to copy the completed message into the - recv_msg before we release the lock. - Otherwise, race conditions may bite us. I - know that's pretty paranoid, but I prefer - to be correct. */ + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } } else { /* Unknown address type. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1445,11 +1778,14 @@ static int i_ipmi_request(ipmi_user_t user, printk("\n"); } #endif - intf->handlers->sender(intf->send_info, smi_msg, priority); + + handlers->sender(intf->send_info, smi_msg, priority); + rcu_read_unlock(); return 0; out_err: + rcu_read_unlock(); ipmi_free_smi_msg(smi_msg); ipmi_free_recv_msg(recv_msg); return rv; @@ -1479,7 +1815,7 @@ int ipmi_request_settime(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1497,6 +1833,7 @@ int ipmi_request_settime(ipmi_user_t user, retries, retry_time_ms); } +EXPORT_SYMBOL(ipmi_request_settime); int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_addr *addr, @@ -1510,7 +1847,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1528,14 +1865,16 @@ int ipmi_request_supply_msgs(ipmi_user_t user, lun, -1, 0); } +EXPORT_SYMBOL(ipmi_request_supply_msgs); +#ifdef CONFIG_PROC_FS static int ipmb_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = (char *) page; ipmi_smi_t intf = data; int i; - int rv= 0; + int rv = 0; for (i = 0; i < IPMI_MAX_CHANNELS; i++) rv += sprintf(out+rv, "%x ", intf->channels[i].address); @@ -1551,8 +1890,9 @@ static int version_file_read_proc(char *page, char **start, off_t off, char *out = (char *) page; ipmi_smi_t intf = data; - return sprintf(out, "%d.%d\n", - intf->version_major, intf->version_minor); + return sprintf(out, "%u.%u\n", + ipmi_version_major(&intf->bmc->id), + ipmi_version_minor(&intf->bmc->id)); } static int stat_file_read_proc(char *page, char **start, off_t off, @@ -1561,65 +1901,70 @@ static int stat_file_read_proc(char *page, char **start, off_t off, char *out = (char *) page; ipmi_smi_t intf = data; - out += sprintf(out, "sent_invalid_commands: %d\n", - intf->sent_invalid_commands); - out += sprintf(out, "sent_local_commands: %d\n", - intf->sent_local_commands); - out += sprintf(out, "handled_local_responses: %d\n", - intf->handled_local_responses); - out += sprintf(out, "unhandled_local_responses: %d\n", - intf->unhandled_local_responses); - out += sprintf(out, "sent_ipmb_commands: %d\n", - intf->sent_ipmb_commands); - out += sprintf(out, "sent_ipmb_command_errs: %d\n", - intf->sent_ipmb_command_errs); - out += sprintf(out, "retransmitted_ipmb_commands: %d\n", - intf->retransmitted_ipmb_commands); - out += sprintf(out, "timed_out_ipmb_commands: %d\n", - intf->timed_out_ipmb_commands); - out += sprintf(out, "timed_out_ipmb_broadcasts: %d\n", - intf->timed_out_ipmb_broadcasts); - out += sprintf(out, "sent_ipmb_responses: %d\n", - intf->sent_ipmb_responses); - out += sprintf(out, "handled_ipmb_responses: %d\n", - intf->handled_ipmb_responses); - out += sprintf(out, "invalid_ipmb_responses: %d\n", - intf->invalid_ipmb_responses); - out += sprintf(out, "unhandled_ipmb_responses: %d\n", - intf->unhandled_ipmb_responses); - out += sprintf(out, "sent_lan_commands: %d\n", - intf->sent_lan_commands); - out += sprintf(out, "sent_lan_command_errs: %d\n", - intf->sent_lan_command_errs); - out += sprintf(out, "retransmitted_lan_commands: %d\n", - intf->retransmitted_lan_commands); - out += sprintf(out, "timed_out_lan_commands: %d\n", - intf->timed_out_lan_commands); - out += sprintf(out, "sent_lan_responses: %d\n", - intf->sent_lan_responses); - out += sprintf(out, "handled_lan_responses: %d\n", - intf->handled_lan_responses); - out += sprintf(out, "invalid_lan_responses: %d\n", - intf->invalid_lan_responses); - out += sprintf(out, "unhandled_lan_responses: %d\n", - intf->unhandled_lan_responses); - out += sprintf(out, "handled_commands: %d\n", - intf->handled_commands); - out += sprintf(out, "invalid_commands: %d\n", - intf->invalid_commands); - out += sprintf(out, "unhandled_commands: %d\n", - intf->unhandled_commands); - out += sprintf(out, "invalid_events: %d\n", - intf->invalid_events); - out += sprintf(out, "events: %d\n", - intf->events); + out += sprintf(out, "sent_invalid_commands: %u\n", + ipmi_get_stat(intf, sent_invalid_commands)); + out += sprintf(out, "sent_local_commands: %u\n", + ipmi_get_stat(intf, sent_local_commands)); + out += sprintf(out, "handled_local_responses: %u\n", + ipmi_get_stat(intf, handled_local_responses)); + out += sprintf(out, "unhandled_local_responses: %u\n", + ipmi_get_stat(intf, unhandled_local_responses)); + out += sprintf(out, "sent_ipmb_commands: %u\n", + ipmi_get_stat(intf, sent_ipmb_commands)); + out += sprintf(out, "sent_ipmb_command_errs: %u\n", + ipmi_get_stat(intf, sent_ipmb_command_errs)); + out += sprintf(out, "retransmitted_ipmb_commands: %u\n", + ipmi_get_stat(intf, retransmitted_ipmb_commands)); + out += sprintf(out, "timed_out_ipmb_commands: %u\n", + ipmi_get_stat(intf, timed_out_ipmb_commands)); + out += sprintf(out, "timed_out_ipmb_broadcasts: %u\n", + ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); + out += sprintf(out, "sent_ipmb_responses: %u\n", + ipmi_get_stat(intf, sent_ipmb_responses)); + out += sprintf(out, "handled_ipmb_responses: %u\n", + ipmi_get_stat(intf, handled_ipmb_responses)); + out += sprintf(out, "invalid_ipmb_responses: %u\n", + ipmi_get_stat(intf, invalid_ipmb_responses)); + out += sprintf(out, "unhandled_ipmb_responses: %u\n", + ipmi_get_stat(intf, unhandled_ipmb_responses)); + out += sprintf(out, "sent_lan_commands: %u\n", + ipmi_get_stat(intf, sent_lan_commands)); + out += sprintf(out, "sent_lan_command_errs: %u\n", + ipmi_get_stat(intf, sent_lan_command_errs)); + out += sprintf(out, "retransmitted_lan_commands: %u\n", + ipmi_get_stat(intf, retransmitted_lan_commands)); + out += sprintf(out, "timed_out_lan_commands: %u\n", + ipmi_get_stat(intf, timed_out_lan_commands)); + out += sprintf(out, "sent_lan_responses: %u\n", + ipmi_get_stat(intf, sent_lan_responses)); + out += sprintf(out, "handled_lan_responses: %u\n", + ipmi_get_stat(intf, handled_lan_responses)); + out += sprintf(out, "invalid_lan_responses: %u\n", + ipmi_get_stat(intf, invalid_lan_responses)); + out += sprintf(out, "unhandled_lan_responses: %u\n", + ipmi_get_stat(intf, unhandled_lan_responses)); + out += sprintf(out, "handled_commands: %u\n", + ipmi_get_stat(intf, handled_commands)); + out += sprintf(out, "invalid_commands: %u\n", + ipmi_get_stat(intf, invalid_commands)); + out += sprintf(out, "unhandled_commands: %u\n", + ipmi_get_stat(intf, unhandled_commands)); + out += sprintf(out, "invalid_events: %u\n", + ipmi_get_stat(intf, invalid_events)); + out += sprintf(out, "events: %u\n", + ipmi_get_stat(intf, events)); + out += sprintf(out, "failed rexmit LAN msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_lan_commands)); + out += sprintf(out, "failed rexmit IPMB msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_ipmb_commands)); return (out - ((char *) page)); } +#endif /* CONFIG_PROC_FS */ 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) + read_proc_t *read_proc, + void *data) { int rv = 0; #ifdef CONFIG_PROC_FS @@ -1643,22 +1988,20 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, kfree(entry); rv = -ENOMEM; } else { - file->nlink = 1; file->data = data; file->read_proc = read_proc; - file->write_proc = write_proc; - file->owner = owner; - spin_lock(&smi->proc_entry_lock); + mutex_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); + mutex_unlock(&smi->proc_entry_lock); } #endif /* CONFIG_PROC_FS */ return rv; } +EXPORT_SYMBOL(ipmi_smi_add_proc_entry); static int add_proc_entries(ipmi_smi_t smi, int num) { @@ -1669,46 +2012,626 @@ static int add_proc_entries(ipmi_smi_t smi, int num) smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; - else { - smi->proc_dir->owner = THIS_MODULE; - } if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", - stat_file_read_proc, NULL, - smi, THIS_MODULE); + stat_file_read_proc, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", - ipmb_file_read_proc, NULL, - smi, THIS_MODULE); + ipmb_file_read_proc, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", - version_file_read_proc, NULL, - smi, THIS_MODULE); + version_file_read_proc, + smi); +#endif /* CONFIG_PROC_FS */ + + return rv; +} + +static void remove_proc_entries(ipmi_smi_t smi) +{ +#ifdef CONFIG_PROC_FS + struct ipmi_proc_entry *entry; + + mutex_lock(&smi->proc_entry_lock); + while (smi->proc_entries) { + entry = smi->proc_entries; + smi->proc_entries = entry->next; + + remove_proc_entry(entry->name, smi->proc_dir); + kfree(entry->name); + kfree(entry); + } + mutex_unlock(&smi->proc_entry_lock); + remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); #endif /* CONFIG_PROC_FS */ +} + +static int __find_bmc_guid(struct device *dev, void *data) +{ + unsigned char *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + return memcmp(bmc->guid, id, 16) == 0; +} + +static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, + unsigned char *guid) +{ + struct device *dev; + + dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +struct prod_dev_id { + unsigned int product_id; + unsigned char device_id; +}; + +static int __find_bmc_prod_dev_id(struct device *dev, void *data) +{ + struct prod_dev_id *id = data; + struct bmc_device *bmc = dev_get_drvdata(dev); + + return (bmc->id.product_id == id->product_id + && bmc->id.device_id == id->device_id); +} + +static struct bmc_device *ipmi_find_bmc_prod_dev_id( + struct device_driver *drv, + unsigned int product_id, unsigned char device_id) +{ + struct prod_dev_id id = { + .product_id = product_id, + .device_id = device_id, + }; + struct device *dev; + + dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); + if (dev) + return dev_get_drvdata(dev); + else + return NULL; +} + +static ssize_t device_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", bmc->id.device_id); +} + +static ssize_t provides_dev_sdrs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%u\n", + (bmc->id.device_revision & 0x80) >> 7); +} + +static ssize_t revision_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u\n", + bmc->id.device_revision & 0x0F); +} + +static ssize_t firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, + bmc->id.firmware_revision_2); +} + +static ssize_t ipmi_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "%u.%u\n", + ipmi_version_major(&bmc->id), + ipmi_version_minor(&bmc->id)); +} + +static ssize_t add_dev_support_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%02x\n", + bmc->id.additional_device_support); +} + +static ssize_t manufacturer_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); +} + +static ssize_t product_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); +} + +static ssize_t aux_firmware_rev_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", + bmc->id.aux_firmware_revision[3], + bmc->id.aux_firmware_revision[2], + bmc->id.aux_firmware_revision[1], + bmc->id.aux_firmware_revision[0]); +} + +static ssize_t guid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bmc_device *bmc = dev_get_drvdata(dev); + + return snprintf(buf, 100, "%Lx%Lx\n", + (long long) bmc->guid[0], + (long long) bmc->guid[8]); +} + +static void remove_files(struct bmc_device *bmc) +{ + if (!bmc->dev) + return; + + device_remove_file(&bmc->dev->dev, + &bmc->device_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + device_remove_file(&bmc->dev->dev, + &bmc->revision_attr); + device_remove_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + device_remove_file(&bmc->dev->dev, + &bmc->version_attr); + device_remove_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + device_remove_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + device_remove_file(&bmc->dev->dev, + &bmc->product_id_attr); + + if (bmc->id.aux_firmware_revision_set) + device_remove_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (bmc->guid_set) + device_remove_file(&bmc->dev->dev, + &bmc->guid_attr); +} + +static void +cleanup_bmc_device(struct kref *ref) +{ + struct bmc_device *bmc; + + bmc = container_of(ref, struct bmc_device, refcount); + + remove_files(bmc); + platform_device_unregister(bmc->dev); + kfree(bmc); +} + +static void ipmi_bmc_unregister(ipmi_smi_t intf) +{ + struct bmc_device *bmc = intf->bmc; + + if (intf->sysfs_name) { + sysfs_remove_link(&intf->si_dev->kobj, intf->sysfs_name); + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; + } + if (intf->my_dev_name) { + sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name); + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + } + + mutex_lock(&ipmidriver_mutex); + kref_put(&bmc->refcount, cleanup_bmc_device); + intf->bmc = NULL; + mutex_unlock(&ipmidriver_mutex); +} + +static int create_files(struct bmc_device *bmc) +{ + int err; + + bmc->device_id_attr.attr.name = "device_id"; + bmc->device_id_attr.attr.mode = S_IRUGO; + bmc->device_id_attr.show = device_id_show; + + bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; + bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; + bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; + + bmc->revision_attr.attr.name = "revision"; + bmc->revision_attr.attr.mode = S_IRUGO; + bmc->revision_attr.show = revision_show; + + bmc->firmware_rev_attr.attr.name = "firmware_revision"; + bmc->firmware_rev_attr.attr.mode = S_IRUGO; + bmc->firmware_rev_attr.show = firmware_rev_show; + + bmc->version_attr.attr.name = "ipmi_version"; + bmc->version_attr.attr.mode = S_IRUGO; + bmc->version_attr.show = ipmi_version_show; + + bmc->add_dev_support_attr.attr.name = "additional_device_support"; + bmc->add_dev_support_attr.attr.mode = S_IRUGO; + bmc->add_dev_support_attr.show = add_dev_support_show; + + bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; + bmc->manufacturer_id_attr.attr.mode = S_IRUGO; + bmc->manufacturer_id_attr.show = manufacturer_id_show; + + bmc->product_id_attr.attr.name = "product_id"; + bmc->product_id_attr.attr.mode = S_IRUGO; + bmc->product_id_attr.show = product_id_show; + + bmc->guid_attr.attr.name = "guid"; + bmc->guid_attr.attr.mode = S_IRUGO; + bmc->guid_attr.show = guid_show; + + bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; + bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; + bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; + + err = device_create_file(&bmc->dev->dev, + &bmc->device_id_attr); + if (err) + goto out; + err = device_create_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + if (err) + goto out_devid; + err = device_create_file(&bmc->dev->dev, + &bmc->revision_attr); + if (err) + goto out_sdrs; + err = device_create_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + if (err) + goto out_rev; + err = device_create_file(&bmc->dev->dev, + &bmc->version_attr); + if (err) + goto out_firm; + err = device_create_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + if (err) + goto out_version; + err = device_create_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + if (err) + goto out_add_dev; + err = device_create_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (err) + goto out_manu; + if (bmc->id.aux_firmware_revision_set) { + err = device_create_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (err) + goto out_prod_id; + } + if (bmc->guid_set) { + err = device_create_file(&bmc->dev->dev, + &bmc->guid_attr); + if (err) + goto out_aux_firm; + } + + return 0; + +out_aux_firm: + if (bmc->id.aux_firmware_revision_set) + device_remove_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); +out_prod_id: + device_remove_file(&bmc->dev->dev, + &bmc->product_id_attr); +out_manu: + device_remove_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); +out_add_dev: + device_remove_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); +out_version: + device_remove_file(&bmc->dev->dev, + &bmc->version_attr); +out_firm: + device_remove_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); +out_rev: + device_remove_file(&bmc->dev->dev, + &bmc->revision_attr); +out_sdrs: + device_remove_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); +out_devid: + device_remove_file(&bmc->dev->dev, + &bmc->device_id_attr); +out: + return err; +} + +static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, + const char *sysfs_name) +{ + int rv; + struct bmc_device *bmc = intf->bmc; + struct bmc_device *old_bmc; + int size; + char dummy[1]; + + mutex_lock(&ipmidriver_mutex); + + /* + * Try to find if there is an bmc_device struct + * representing the interfaced BMC already + */ + if (bmc->guid_set) + old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid); + else + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, + bmc->id.product_id, + bmc->id.device_id); + + /* + * If there is already an bmc_device, free the new one, + * otherwise register the new BMC device + */ + if (old_bmc) { + kfree(bmc); + intf->bmc = old_bmc; + bmc = old_bmc; + + kref_get(&bmc->refcount); + mutex_unlock(&ipmidriver_mutex); + + printk(KERN_INFO + "ipmi: interfacing existing BMC (man_id: 0x%6.6x," + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } else { + char name[14]; + unsigned char orig_dev_id = bmc->id.device_id; + int warn_printed = 0; + + snprintf(name, sizeof(name), + "ipmi_bmc.%4.4x", bmc->id.product_id); + + while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, + bmc->id.product_id, + bmc->id.device_id)) { + if (!warn_printed) { + printk(KERN_WARNING PFX + "This machine has two different BMCs" + " with the same product id and device" + " id. This is an error in the" + " firmware, but incrementing the" + " device id to work around the problem." + " Prod ID = 0x%x, Dev ID = 0x%x\n", + bmc->id.product_id, bmc->id.device_id); + warn_printed = 1; + } + bmc->id.device_id++; /* Wraps at 255 */ + if (bmc->id.device_id == orig_dev_id) { + printk(KERN_ERR PFX + "Out of device ids!\n"); + break; + } + } + + bmc->dev = platform_device_alloc(name, bmc->id.device_id); + if (!bmc->dev) { + mutex_unlock(&ipmidriver_mutex); + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to allocate platform device\n"); + return -ENOMEM; + } + bmc->dev->dev.driver = &ipmidriver.driver; + dev_set_drvdata(&bmc->dev->dev, bmc); + kref_init(&bmc->refcount); + + rv = platform_device_add(bmc->dev); + mutex_unlock(&ipmidriver_mutex); + if (rv) { + platform_device_put(bmc->dev); + bmc->dev = NULL; + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to register bmc device: %d\n", + rv); + /* + * Don't go to out_err, you can only do that if + * the device is registered already. + */ + return rv; + } + + rv = create_files(bmc); + if (rv) { + mutex_lock(&ipmidriver_mutex); + platform_device_unregister(bmc->dev); + mutex_unlock(&ipmidriver_mutex); + + return rv; + } + + printk(KERN_INFO + "ipmi: Found new BMC (man_id: 0x%6.6x, " + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); + } + + /* + * create symlink from system interface device to bmc device + * and back. + */ + intf->sysfs_name = kstrdup(sysfs_name, GFP_KERNEL); + if (!intf->sysfs_name) { + rv = -ENOMEM; + printk(KERN_ERR + "ipmi_msghandler: allocate link to BMC: %d\n", + rv); + goto out_err; + } + + rv = sysfs_create_link(&intf->si_dev->kobj, + &bmc->dev->dev.kobj, intf->sysfs_name); + if (rv) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; + printk(KERN_ERR + "ipmi_msghandler: Unable to create bmc symlink: %d\n", + rv); + goto out_err; + } + + size = snprintf(dummy, 0, "ipmi%d", ifnum); + intf->my_dev_name = kmalloc(size+1, GFP_KERNEL); + if (!intf->my_dev_name) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; + rv = -ENOMEM; + printk(KERN_ERR + "ipmi_msghandler: allocate link from BMC: %d\n", + rv); + goto out_err; + } + snprintf(intf->my_dev_name, size+1, "ipmi%d", ifnum); + + rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj, + intf->my_dev_name); + if (rv) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + printk(KERN_ERR + "ipmi_msghandler:" + " Unable to create symlink to bmc: %d\n", + rv); + goto out_err; + } + + return 0; + +out_err: + ipmi_bmc_unregister(intf); + return rv; +} + +static int +send_guid_cmd(ipmi_smi_t intf, int chan) +{ + struct kernel_ipmi_msg msg; + struct ipmi_system_interface_addr si; + + si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + si.channel = IPMI_BMC_CHANNEL; + si.lun = 0; + + msg.netfn = IPMI_NETFN_APP_REQUEST; + msg.cmd = IPMI_GET_DEVICE_GUID_CMD; + msg.data = NULL; + msg.data_len = 0; + return i_ipmi_request(NULL, + intf, + (struct ipmi_addr *) &si, + 0, + &msg, + intf, + NULL, + NULL, + 0, + intf->channels[0].address, + intf->channels[0].lun, + -1, 0); +} + +static void +guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +{ + if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) + || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) + || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) + /* Not for me */ + return; + + if (msg->msg.data[0] != 0) { + /* Error from getting the GUID, the BMC doesn't have one. */ + intf->bmc->guid_set = 0; + goto out; + } - return rv; + if (msg->msg.data_len < 17) { + intf->bmc->guid_set = 0; + printk(KERN_WARNING PFX + "guid_handler: The GUID response from the BMC was too" + " short, it was %d but should have been 17. Assuming" + " GUID is not available.\n", + msg->msg.data_len); + goto out; + } + + memcpy(intf->bmc->guid, msg->msg.data, 16); + intf->bmc->guid_set = 1; + out: + wake_up(&intf->waitq); } -static void remove_proc_entries(ipmi_smi_t smi) +static void +get_guid(ipmi_smi_t intf) { -#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; + int rv; - remove_proc_entry(entry->name, smi->proc_dir); - 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 */ + intf->bmc->guid_set = 0x2; + intf->null_user_handler = guid_handler; + rv = send_guid_cmd(intf, 0); + if (rv) + /* Send failed, no GUID available. */ + intf->bmc->guid_set = 0; + wait_event(intf->waitq, intf->bmc->guid_set != 2); + intf->null_user_handler = NULL; } static int @@ -1749,17 +2672,18 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) - && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) - { + && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) { /* It's the one we want */ if (msg->msg.data[0] != 0) { /* Got an error from the channel, just go on. */ if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) { - /* If the MC does not support this - command, that is legal. We just - assume it has one IPMB at channel - zero. */ + /* + * If the MC does not support this + * command, that is legal. We just + * assume it has one IPMB at channel + * zero. + */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol @@ -1780,7 +2704,7 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) intf->channels[chan].medium = msg->msg.data[2] & 0x7f; intf->channels[chan].protocol = msg->msg.data[3] & 0x1f; - next_channel: + next_channel: intf->curr_channel++; if (intf->curr_channel >= IPMI_MAX_CHANNELS) wake_up(&intf->waitq); @@ -1801,39 +2725,60 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } +void ipmi_poll_interface(ipmi_user_t user) +{ + ipmi_smi_t intf = user->intf; + + if (intf->handlers->poll) + intf->handlers->poll(intf->send_info); +} +EXPORT_SYMBOL(ipmi_poll_interface); + int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, - unsigned char version_major, - unsigned char version_minor, - unsigned char slave_addr, - ipmi_smi_t *new_intf) + struct ipmi_device_id *device_id, + struct device *si_dev, + const char *sysfs_name, + unsigned char slave_addr) { int i, j; int rv; ipmi_smi_t intf; - unsigned long flags; + ipmi_smi_t tintf; + struct list_head *link; - - /* Make sure the driver is actually initialized, this handles - problems with initialization order. */ + /* + * Make sure the driver is actually initialized, this handles + * problems with initialization order. + */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; - /* The init code doesn't return an error if it was turned - off, but it won't initialize. Check that. */ + /* + * The init code doesn't return an error if it was turned + * off, but it won't initialize. Check that. + */ if (!initialized) return -ENODEV; } - intf = kmalloc(sizeof(*intf), GFP_KERNEL); + intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) return -ENOMEM; - memset(intf, 0, sizeof(*intf)); - intf->intf_num = -1; + + intf->ipmi_version_major = ipmi_version_major(device_id); + intf->ipmi_version_minor = ipmi_version_minor(device_id); + + intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); + if (!intf->bmc) { + kfree(intf); + return -ENOMEM; + } + intf->intf_num = -1; /* Mark it invalid for now. */ kref_init(&intf->refcount); - intf->version_major = version_major; - intf->version_minor = version_minor; + intf->bmc->id = *device_id; + intf->si_dev = si_dev; for (j = 0; j < IPMI_MAX_CHANNELS; j++) { intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; intf->channels[j].lun = 2; @@ -1850,44 +2795,53 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, } intf->curr_seq = 0; #ifdef CONFIG_PROC_FS - spin_lock_init(&intf->proc_entry_lock); + mutex_init(&intf->proc_entry_lock); #endif spin_lock_init(&intf->waiting_msgs_lock); INIT_LIST_HEAD(&intf->waiting_msgs); spin_lock_init(&intf->events_lock); INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; - init_MUTEX(&intf->cmd_rcvrs_lock); + mutex_init(&intf->cmd_rcvrs_mutex); + spin_lock_init(&intf->maintenance_mode_lock); INIT_LIST_HEAD(&intf->cmd_rcvrs); init_waitqueue_head(&intf->waitq); + for (i = 0; i < IPMI_NUM_STATS; i++) + atomic_set(&intf->stats[i], 0); - spin_lock_init(&intf->counter_lock); intf->proc_dir = NULL; - rv = -ENOMEM; - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - if (ipmi_interfaces[i] == NULL) { - intf->intf_num = i; - /* Reserve the entry till we are done. */ - ipmi_interfaces[i] = IPMI_INVALID_INTERFACE_ENTRY; - rv = 0; + mutex_lock(&smi_watchers_mutex); + mutex_lock(&ipmi_interfaces_mutex); + /* Look for a hole in the numbers. */ + i = 0; + link = &ipmi_interfaces; + list_for_each_entry_rcu(tintf, &ipmi_interfaces, link) { + if (tintf->intf_num != i) { + link = &tintf->link; break; } + i++; } - spin_unlock_irqrestore(&interfaces_lock, flags); + /* Add the new interface in numeric order. */ + if (i == 0) + list_add_rcu(&intf->link, &ipmi_interfaces); + else + list_add_tail_rcu(&intf->link, link); + + rv = handlers->start_processing(send_info, intf); if (rv) goto out; - /* FIXME - this is an ugly kludge, this sets the intf for the - caller before sending any messages with it. */ - *new_intf = intf; + get_guid(intf); - if ((version_major > 1) - || ((version_major == 1) && (version_minor >= 5))) - { - /* Start scanning the channels to see what is - available. */ + if ((intf->ipmi_version_major > 1) + || ((intf->ipmi_version_major == 1) + && (intf->ipmi_version_minor >= 5))) { + /* + * Start scanning the channels to see what is + * available. + */ intf->null_user_handler = channel_handler; intf->curr_channel = 0; rv = send_channel_info_cmd(intf, 0); @@ -1897,6 +2851,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, /* Wait for the channel info to be read. */ wait_event(intf->waitq, intf->curr_channel >= IPMI_MAX_CHANNELS); + intf->null_user_handler = NULL; } else { /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; @@ -1906,80 +2861,95 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (rv == 0) rv = add_proc_entries(intf, i); + rv = ipmi_bmc_register(intf, i, sysfs_name); + out: if (rv) { if (intf->proc_dir) remove_proc_entries(intf); + intf->handlers = NULL; + list_del_rcu(&intf->link); + mutex_unlock(&ipmi_interfaces_mutex); + mutex_unlock(&smi_watchers_mutex); + synchronize_rcu(); kref_put(&intf->refcount, intf_free); - if (i < MAX_IPMI_INTERFACES) { - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = NULL; - spin_unlock_irqrestore(&interfaces_lock, flags); - } } else { - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = intf; - spin_unlock_irqrestore(&interfaces_lock, flags); - call_smi_watchers(i); + /* + * Keep memory order straight for RCU readers. Make + * sure everything else is committed to memory before + * setting intf_num to mark the interface valid. + */ + smp_wmb(); + intf->intf_num = i; + mutex_unlock(&ipmi_interfaces_mutex); + /* After this point the interface is legal to use. */ + call_smi_watchers(i, intf->si_dev); + mutex_unlock(&smi_watchers_mutex); } return rv; } +EXPORT_SYMBOL(ipmi_register_smi); + +static void cleanup_smi_msgs(ipmi_smi_t intf) +{ + int i; + struct seq_table *ent; + + /* No need for locks, the interface is down. */ + for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { + ent = &(intf->seq_table[i]); + if (!ent->inuse) + continue; + deliver_err_response(ent->recv_msg, IPMI_ERR_UNSPECIFIED); + } +} int ipmi_unregister_smi(ipmi_smi_t intf) { - int i; struct ipmi_smi_watcher *w; - unsigned long flags; - - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - if (ipmi_interfaces[i] == intf) { - /* Set the interface number reserved until we - * are done. */ - ipmi_interfaces[i] = IPMI_INVALID_INTERFACE_ENTRY; - intf->intf_num = -1; - break; - } - } - spin_unlock_irqrestore(&interfaces_lock,flags); + int intf_num = intf->intf_num; + + ipmi_bmc_unregister(intf); - if (i == MAX_IPMI_INTERFACES) - return -ENODEV; + mutex_lock(&smi_watchers_mutex); + mutex_lock(&ipmi_interfaces_mutex); + intf->intf_num = -1; + intf->handlers = NULL; + list_del_rcu(&intf->link); + mutex_unlock(&ipmi_interfaces_mutex); + synchronize_rcu(); + + cleanup_smi_msgs(intf); remove_proc_entries(intf); - /* Call all the watcher interfaces to tell them that - an interface is gone. */ - down_read(&smi_watchers_sem); + /* + * Call all the watcher interfaces to tell them that + * an interface is gone. + */ list_for_each_entry(w, &smi_watchers, link) - w->smi_gone(i); - up_read(&smi_watchers_sem); - - /* Allow the entry to be reused now. */ - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = NULL; - spin_unlock_irqrestore(&interfaces_lock,flags); + w->smi_gone(intf_num); + mutex_unlock(&smi_watchers_mutex); kref_put(&intf->refcount, intf_free); return 0; } +EXPORT_SYMBOL(ipmi_unregister_smi); static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_addr ipmb_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; - - /* This is 11, not 10, because the response must contain a - * completion code. */ + /* + * This is 11, not 10, because the response must contain a + * completion code. + */ if (msg->rsp_size < 11) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_ipmb_responses); return 0; } @@ -1993,37 +2963,38 @@ static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, ipmb_addr.channel = msg->rsp[3] & 0x0f; ipmb_addr.lun = msg->rsp[7] & 3; - /* It's a response from a remote entity. Look up the sequence - number and handle the response. */ + /* + * It's a response from a remote entity. Look up the sequence + * number and handle the response. + */ if (intf_find_seq(intf, msg->rsp[7] >> 2, msg->rsp[3] & 0x0f, msg->rsp[8], (msg->rsp[4] >> 2) & (~1), (struct ipmi_addr *) &(ipmb_addr), - &recv_msg)) - { - /* We were unable to find the sequence number, - so just nuke the message. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + &recv_msg)) { + /* + * We were unable to find the sequence number, + * so just nuke the message. + */ + ipmi_inc_stat(intf, unhandled_ipmb_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 9); - /* THe other fields matched, so no need to set them, except - for netfn, which needs to be the response that was - returned, not the request value. */ + /* + * The other fields matched, so no need to set them, except + * for netfn, which needs to be the response that was + * returned, not the request value. + */ recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_ipmb_responses); deliver_response(recv_msg); return 0; @@ -2036,16 +3007,15 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; + struct ipmi_smi_handlers *handlers; if (msg->rsp_size < 10) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_commands); return 0; } @@ -2056,9 +3026,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[4] >> 2; cmd = msg->rsp[8]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); @@ -2068,19 +3039,17 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, if (user == NULL) { /* We didn't find a user, deliver an error response. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_commands); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); msg->data[1] = IPMI_SEND_MSG_CMD; msg->data[2] = msg->rsp[3]; msg->data[3] = msg->rsp[6]; - msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); + msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address; - /* rqseq/lun */ - msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); + /* rqseq/lun */ + msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data[10] = ipmb_checksum(&(msg->data[6]), 4); @@ -2095,21 +3064,29 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, printk("\n"); } #endif - intf->handlers->sender(intf->send_info, msg, 0); - - rv = -1; /* We used the message, so return the value that - causes it to not be freed or queued. */ + rcu_read_lock(); + handlers = intf->handlers; + if (handlers) { + handlers->sender(intf->send_info, msg, 0); + /* + * We used the message, so return the value + * that causes it to not be freed or + * queued. + */ + rv = -1; + } + rcu_read_unlock(); } else { /* Deliver the message to the user. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; kref_put(&user->refcount, free_user); } else { @@ -2120,8 +3097,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, ipmb_addr->lun = msg->rsp[7] & 3; ipmb_addr->channel = msg->rsp[3] & 0xf; - /* Extract the rest of the message information - from the IPMB header.*/ + /* + * Extract the rest of the message information + * from the IPMB header. + */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[7] >> 2; @@ -2129,8 +3108,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, recv_msg->msg.cmd = msg->rsp[8]; recv_msg->msg.data = recv_msg->msg_data; - /* We chop off 10, not 9 bytes because the checksum - at the end also needs to be removed. */ + /* + * We chop off 10, not 9 bytes because the checksum + * at the end also needs to be removed. + */ recv_msg->msg.data_len = msg->rsp_size - 10; memcpy(recv_msg->msg_data, &(msg->rsp[9]), @@ -2147,16 +3128,15 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, { struct ipmi_lan_addr lan_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; - /* This is 13, not 12, because the response must contain a - * completion code. */ + /* + * This is 13, not 12, because the response must contain a + * completion code. + */ if (msg->rsp_size < 13) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_lan_responses); return 0; } @@ -2173,37 +3153,38 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, lan_addr.privilege = msg->rsp[3] >> 4; lan_addr.lun = msg->rsp[9] & 3; - /* It's a response from a remote entity. Look up the sequence - number and handle the response. */ + /* + * It's a response from a remote entity. Look up the sequence + * number and handle the response. + */ if (intf_find_seq(intf, msg->rsp[9] >> 2, msg->rsp[3] & 0x0f, msg->rsp[10], (msg->rsp[6] >> 2) & (~1), (struct ipmi_addr *) &(lan_addr), - &recv_msg)) - { - /* We were unable to find the sequence number, - so just nuke the message. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + &recv_msg)) { + /* + * We were unable to find the sequence number, + * so just nuke the message. + */ + ipmi_inc_stat(intf, unhandled_lan_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[11]), msg->rsp_size - 11); - /* The other fields matched, so no need to set them, except - for netfn, which needs to be the response that was - returned, not the request value. */ + /* + * The other fields matched, so no need to set them, except + * for netfn, which needs to be the response that was + * returned, not the request value. + */ recv_msg->msg.netfn = msg->rsp[6] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 12; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_lan_responses); deliver_response(recv_msg); return 0; @@ -2216,16 +3197,14 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_lan_addr *lan_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; if (msg->rsp_size < 12) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_commands); return 0; } @@ -2236,9 +3215,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[6] >> 2; cmd = msg->rsp[10]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); @@ -2248,23 +3228,23 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, if (user == NULL) { /* We didn't find a user, just give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_commands); - rv = 0; /* Don't do anything with these messages, just - allow them to be freed. */ + /* + * Don't do anything with these messages, just allow + * them to be freed. + */ + rv = 0; } else { /* Deliver the message to the user. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling later. + */ rv = 1; kref_put(&user->refcount, free_user); } else { @@ -2278,8 +3258,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, lan_addr->channel = msg->rsp[3] & 0xf; lan_addr->privilege = msg->rsp[3] >> 4; - /* Extract the rest of the message information - from the IPMB header.*/ + /* + * Extract the rest of the message information + * from the IPMB header. + */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[9] >> 2; @@ -2287,8 +3269,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, recv_msg->msg.cmd = msg->rsp[10]; recv_msg->msg.data = recv_msg->msg_data; - /* We chop off 12, not 11 bytes because the checksum - at the end also needs to be removed. */ + /* + * We chop off 12, not 11 bytes because the checksum + * at the end also needs to be removed. + */ recv_msg->msg.data_len = msg->rsp_size - 12; memcpy(recv_msg->msg_data, &(msg->rsp[11]), @@ -2300,11 +3284,119 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, return rv; } +/* + * This routine will handle "Get Message" command responses with + * channels that use an OEM Medium. The message format belongs to + * the OEM. See IPMI 2.0 specification, Chapter 6 and + * Chapter 22, sections 22.6 and 22.24 for more details. + */ +static int handle_oem_get_msg_cmd(ipmi_smi_t intf, + struct ipmi_smi_msg *msg) +{ + struct cmd_rcvr *rcvr; + int rv = 0; + unsigned char netfn; + unsigned char cmd; + unsigned char chan; + ipmi_user_t user = NULL; + struct ipmi_system_interface_addr *smi_addr; + struct ipmi_recv_msg *recv_msg; + + /* + * We expect the OEM SW to perform error checking + * so we just do some basic sanity checks + */ + if (msg->rsp_size < 4) { + /* Message not big enough, just ignore it. */ + ipmi_inc_stat(intf, invalid_commands); + return 0; + } + + if (msg->rsp[2] != 0) { + /* An error getting the response, just ignore it. */ + return 0; + } + + /* + * This is an OEM Message so the OEM needs to know how + * handle the message. We do no interpretation. + */ + netfn = msg->rsp[0] >> 2; + cmd = msg->rsp[1]; + chan = msg->rsp[3] & 0xf; + + rcu_read_lock(); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); + if (rcvr) { + user = rcvr->user; + kref_get(&user->refcount); + } else + user = NULL; + rcu_read_unlock(); + + if (user == NULL) { + /* We didn't find a user, just give up. */ + ipmi_inc_stat(intf, unhandled_commands); + + /* + * Don't do anything with these messages, just allow + * them to be freed. + */ + + rv = 0; + } else { + /* Deliver the message to the user. */ + ipmi_inc_stat(intf, handled_commands); + + recv_msg = ipmi_alloc_recv_msg(); + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ + rv = 1; + kref_put(&user->refcount, free_user); + } else { + /* + * OEM Messages are expected to be delivered via + * the system interface to SMS software. We might + * need to visit this again depending on OEM + * requirements + */ + smi_addr = ((struct ipmi_system_interface_addr *) + &(recv_msg->addr)); + smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr->channel = IPMI_BMC_CHANNEL; + smi_addr->lun = msg->rsp[0] & 3; + + recv_msg->user = user; + recv_msg->user_msg_data = NULL; + recv_msg->recv_type = IPMI_OEM_RECV_TYPE; + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[1]; + recv_msg->msg.data = recv_msg->msg_data; + + /* + * The message starts at byte 4 which follows the + * the Channel Byte in the "GET MESSAGE" command + */ + recv_msg->msg.data_len = msg->rsp_size - 4; + memcpy(recv_msg->msg_data, + &(msg->rsp[4]), + msg->rsp_size - 4); + deliver_response(recv_msg); + } + } + + return rv; +} + static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *msg) { struct ipmi_system_interface_addr *smi_addr; - + recv_msg->msgid = 0; smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; @@ -2330,9 +3422,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_events++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_events); return 0; } @@ -2345,27 +3435,30 @@ static int handle_read_event_rsp(ipmi_smi_t intf, spin_lock_irqsave(&intf->events_lock, flags); - spin_lock(&intf->counter_lock); - intf->events++; - spin_unlock(&intf->counter_lock); + ipmi_inc_stat(intf, events); - /* Allocate and fill in one message for every user that is getting - events. */ + /* + * Allocate and fill in one message for every user that is + * getting events. + */ rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->gets_events) + if (!user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { rcu_read_unlock(); - list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { + list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, + link) { list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); } - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; goto out; } @@ -2386,24 +3479,32 @@ static int handle_read_event_rsp(ipmi_smi_t intf, deliver_response(recv_msg); } } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { - /* No one to receive the message, put it in queue if there's - not already too many things in the queue. */ + /* + * No one to receive the message, put it in queue if there's + * not already too many things in the queue. + */ recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; goto out; } copy_event_into_recv_msg(recv_msg, msg); list_add_tail(&(recv_msg->link), &(intf->waiting_events)); - } else { - /* There's too many things in the queue, discard this - message. */ - printk(KERN_WARNING PFX "Event queue full, discarding an" - " incoming event\n"); + intf->waiting_events_count++; + } else if (!intf->event_msg_printed) { + /* + * There's too many things in the queue, discard this + * message. + */ + printk(KERN_WARNING PFX "Event queue full, discarding" + " incoming events\n"); + intf->event_msg_printed = 1; } out: @@ -2416,16 +3517,15 @@ static int handle_bmc_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg; - unsigned long flags; struct ipmi_user *user; recv_msg = (struct ipmi_recv_msg *) msg->user_data; - if (recv_msg == NULL) - { - printk(KERN_WARNING"IPMI message received with no owner. This\n" - "could be because of a malformed message, or\n" - "because of a hardware error. Contact your\n" - "hardware vender for assistance\n"); + if (recv_msg == NULL) { + printk(KERN_WARNING + "IPMI message received with no owner. This\n" + "could be because of a malformed message, or\n" + "because of a hardware error. Contact your\n" + "hardware vender for assistance\n"); return 0; } @@ -2433,16 +3533,12 @@ static int handle_bmc_rsp(ipmi_smi_t intf, /* Make sure the user still exists. */ if (user && !user->valid) { /* The user for the message went away, so give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_local_responses); ipmi_free_recv_msg(recv_msg); } else { struct ipmi_system_interface_addr *smi_addr; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_local_responses); recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; recv_msg->msgid = msg->msgid; smi_addr = ((struct ipmi_system_interface_addr *) @@ -2463,9 +3559,11 @@ static int handle_bmc_rsp(ipmi_smi_t intf, return 0; } -/* Handle a new message. Return 1 if the message should be requeued, - 0 if the message should be freed, or -1 if the message should not - be freed or requeued. */ +/* + * Handle a new message. Return 1 if the message should be requeued, + * 0 if the message should be freed, or -1 if the message should not + * be freed or requeued. + */ static int handle_new_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { @@ -2490,10 +3588,12 @@ static int handle_new_recv_msg(ipmi_smi_t intf, msg->rsp[1] = msg->data[1]; msg->rsp[2] = IPMI_ERR_UNSPECIFIED; msg->rsp_size = 3; - } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))/* Netfn */ - || (msg->rsp[1] != msg->data[1])) /* Command */ - { - /* The response is not even marginally correct. */ + } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1)) + || (msg->rsp[1] != msg->data[1])) { + /* + * The NetFN and Command in the response is not even + * marginally correct. + */ printk(KERN_WARNING PFX "BMC returned incorrect response," " expected netfn %x cmd %x, got netfn %x cmd %x\n", (msg->data[0] >> 2) | 1, msg->data[1], @@ -2508,10 +3608,11 @@ static int handle_new_recv_msg(ipmi_smi_t intf, if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data != NULL)) - { - /* It's a response to a response we sent. For this we - deliver a send message response to the user. */ + && (msg->user_data != NULL)) { + /* + * It's a response to a response we sent. For this we + * deliver a send message response to the user. + */ struct ipmi_recv_msg *recv_msg = msg->user_data; requeue = 0; @@ -2537,8 +3638,7 @@ static int handle_new_recv_msg(ipmi_smi_t intf, recv_msg->msg_data[0] = msg->rsp[2]; deliver_response(recv_msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) - && (msg->rsp[1] == IPMI_GET_MSG_CMD)) - { + && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { @@ -2547,15 +3647,30 @@ static int handle_new_recv_msg(ipmi_smi_t intf, goto out; } + /* + ** We need to make sure the channels have been initialized. + ** The channel_handler routine will set the "curr_channel" + ** equal to or greater than IPMI_MAX_CHANNELS when all the + ** channels for this interface have been initialized. + */ + if (intf->curr_channel < IPMI_MAX_CHANNELS) { + requeue = 1; /* Just put the message back for now */ + goto out; + } + switch (intf->channels[chan].medium) { case IPMI_CHANNEL_MEDIUM_IPMB: if (msg->rsp[4] & 0x04) { - /* It's a response, so find the - requesting message and send it up. */ + /* + * It's a response, so find the + * requesting message and send it up. + */ requeue = handle_ipmb_get_msg_rsp(intf, msg); } else { - /* It's a command to the SMS from some other - entity. Handle that. */ + /* + * It's a command to the SMS from some other + * entity. Handle that. + */ requeue = handle_ipmb_get_msg_cmd(intf, msg); } break; @@ -2563,25 +3678,39 @@ static int handle_new_recv_msg(ipmi_smi_t intf, case IPMI_CHANNEL_MEDIUM_8023LAN: case IPMI_CHANNEL_MEDIUM_ASYNC: if (msg->rsp[6] & 0x04) { - /* It's a response, so find the - requesting message and send it up. */ + /* + * It's a response, so find the + * requesting message and send it up. + */ requeue = handle_lan_get_msg_rsp(intf, msg); } else { - /* It's a command to the SMS from some other - entity. Handle that. */ + /* + * It's a command to the SMS from some other + * entity. Handle that. + */ requeue = handle_lan_get_msg_cmd(intf, msg); } break; default: - /* We don't handle the channel type, so just - * free the message. */ - requeue = 0; + /* Check for OEM Channels. Clients had better + register for these commands. */ + if ((intf->channels[chan].medium + >= IPMI_CHANNEL_MEDIUM_OEM_MIN) + && (intf->channels[chan].medium + <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { + requeue = handle_oem_get_msg_cmd(intf, msg); + } else { + /* + * We don't handle the channel type, so just + * free the message. + */ + requeue = 0; + } } } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) - && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) - { + && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { /* It's an asyncronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { @@ -2597,69 +3726,82 @@ static int handle_new_recv_msg(ipmi_smi_t intf, void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { - unsigned long flags; + unsigned long flags = 0; /* keep us warning-free. */ int rv; + int run_to_completion; if ((msg->data_size >= 2) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) && (msg->data[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data == NULL)) - { - /* This is the local response to a command send, start - the timer for these. The user_data will not be - NULL if this is a response send, and we will let - response sends just go through. */ - - /* Check for errors, if we get certain errors (ones - that mean basically we can try again later), we - ignore them and start the timer. Otherwise we - report the error immediately. */ + && (msg->user_data == NULL)) { + /* + * This is the local response to a command send, start + * the timer for these. The user_data will not be + * NULL if this is a response send, and we will let + * response sends just go through. + */ + + /* + * Check for errors, if we get certain errors (ones + * that mean basically we can try again later), we + * ignore them and start the timer. Otherwise we + * report the error immediately. + */ if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0) && (msg->rsp[2] != IPMI_NODE_BUSY_ERR) - && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)) - { + && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) + && (msg->rsp[2] != IPMI_BUS_ERR) + && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { int chan = msg->rsp[3] & 0xf; /* Got an error sending the message, handle it. */ - spin_lock_irqsave(&intf->counter_lock, flags); if (chan >= IPMI_MAX_CHANNELS) ; /* This shouldn't happen */ else if ((intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_8023LAN) || (intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) - intf->sent_lan_command_errs++; + ipmi_inc_stat(intf, sent_lan_command_errs); else - intf->sent_ipmb_command_errs++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_ipmb_command_errs); intf_err_seq(intf, msg->msgid, msg->rsp[2]); - } else { + } else /* The message was sent, start the timer. */ intf_start_seq_timer(intf, msg->msgid); - } ipmi_free_smi_msg(msg); goto out; } - /* To preserve message order, if the list is not empty, we - tack this message onto the end of the list. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + /* + * To preserve message order, if the list is not empty, we + * tack this message onto the end of the list. + */ + run_to_completion = intf->run_to_completion; + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); if (!list_empty(&intf->waiting_msgs)) { list_add_tail(&msg->link, &intf->waiting_msgs); - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); goto out; } - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + rv = handle_new_recv_msg(intf, msg); if (rv > 0) { - /* Could not handle the message now, just add it to a - list to handle later. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + /* + * Could not handle the message now, just add it to a + * list to handle later. + */ + run_to_completion = intf->run_to_completion; + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); list_add_tail(&msg->link, &intf->waiting_msgs); - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); } else if (rv == 0) { ipmi_free_smi_msg(msg); } @@ -2667,6 +3809,7 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, out: return; } +EXPORT_SYMBOL(ipmi_smi_msg_received); void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) { @@ -2674,24 +3817,14 @@ void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->handler->ipmi_watchdog_pretimeout) + if (!user->handler->ipmi_watchdog_pretimeout) continue; user->handler->ipmi_watchdog_pretimeout(user->handler_data); } rcu_read_unlock(); } - -static void -handle_msg_timeout(struct ipmi_recv_msg *msg) -{ - msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - msg->msg_data[0] = IPMI_TIMEOUT_COMPLETION_CODE; - msg->msg.netfn |= 1; /* Convert to a response. */ - msg->msg.data_len = 1; - msg->msg.data = msg->msg_data; - deliver_response(msg); -} +EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); static struct ipmi_smi_msg * smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, @@ -2699,14 +3832,16 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, { struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); if (!smi_msg) - /* If we can't allocate the message, then just return, we - get 4 retries, so this should be ok. */ + /* + * If we can't allocate the message, then just return, we + * get 4 retries, so this should be ok. + */ return NULL; memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); - + #ifdef DEBUG_MSGING { int m; @@ -2723,7 +3858,11 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, struct list_head *timeouts, long timeout_period, int slot, unsigned long *flags) { - struct ipmi_recv_msg *msg; + struct ipmi_recv_msg *msg; + struct ipmi_smi_handlers *handlers; + + if (intf->intf_num == -1) + return; if (!ent->inuse) return; @@ -2737,42 +3876,57 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, ent->inuse = 0; msg = ent->recv_msg; list_add_tail(&msg->link, timeouts); - spin_lock(&intf->counter_lock); if (ent->broadcast) - intf->timed_out_ipmb_broadcasts++; - else if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) - intf->timed_out_lan_commands++; + ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); + else if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, timed_out_lan_commands); else - intf->timed_out_ipmb_commands++; - spin_unlock(&intf->counter_lock); + ipmi_inc_stat(intf, timed_out_ipmb_commands); } else { struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ - /* Start with the max timer, set to normal - timer after the message is sent. */ + /* + * Start with the max timer, set to normal timer after + * the message is sent. + */ ent->timeout = MAX_MSG_TIMEOUT; ent->retries_left--; - spin_lock(&intf->counter_lock); - if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) - intf->retransmitted_lan_commands++; - else - intf->retransmitted_ipmb_commands++; - spin_unlock(&intf->counter_lock); - smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); - if (! smi_msg) + if (!smi_msg) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + dropped_rexmit_lan_commands); + else + ipmi_inc_stat(intf, + dropped_rexmit_ipmb_commands); return; + } spin_unlock_irqrestore(&intf->seq_lock, *flags); - /* Send the new message. We send with a zero - * priority. It timed out, I doubt time is - * that critical now, and high priority - * messages are really only for messages to the - * local MC, which don't get resent. */ - intf->handlers->sender(intf->send_info, - smi_msg, 0); + + /* + * Send the new message. We send with a zero + * priority. It timed out, I doubt time is that + * critical now, and high priority messages are really + * only for messages to the local MC, which don't get + * resent. + */ + handlers = intf->handlers; + if (handlers) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + retransmitted_lan_commands); + else + ipmi_inc_stat(intf, + retransmitted_ipmb_commands); + + intf->handlers->sender(intf->send_info, + smi_msg, 0); + } else + ipmi_free_smi_msg(smi_msg); + spin_lock_irqsave(&intf->seq_lock, *flags); } } @@ -2784,65 +3938,89 @@ static void ipmi_timeout_handler(long timeout_period) struct ipmi_recv_msg *msg, *msg2; struct ipmi_smi_msg *smi_msg, *smi_msg2; unsigned long flags; - int i, j; - - INIT_LIST_HEAD(&timeouts); - - spin_lock(&interfaces_lock); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) - continue; - kref_get(&intf->refcount); - spin_unlock(&interfaces_lock); + int i; + rcu_read_lock(); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { /* See if any waiting messages need to be processed. */ spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, &intf->waiting_msgs, link) { - if (! handle_new_recv_msg(intf, smi_msg)) { + list_for_each_entry_safe(smi_msg, smi_msg2, + &intf->waiting_msgs, link) { + if (!handle_new_recv_msg(intf, smi_msg)) { list_del(&smi_msg->link); ipmi_free_smi_msg(smi_msg); } else { - /* To preserve message order, quit if we - can't handle a message. */ + /* + * To preserve message order, quit if we + * can't handle a message. + */ break; } } spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - /* Go through the seq table and find any messages that - have timed out, putting them in the timeouts - list. */ + /* + * Go through the seq table and find any messages that + * have timed out, putting them in the timeouts + * list. + */ + INIT_LIST_HEAD(&timeouts); spin_lock_irqsave(&intf->seq_lock, flags); - for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) - check_msg_timeout(intf, &(intf->seq_table[j]), - &timeouts, timeout_period, j, + for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) + check_msg_timeout(intf, &(intf->seq_table[i]), + &timeouts, timeout_period, i, &flags); spin_unlock_irqrestore(&intf->seq_lock, flags); list_for_each_entry_safe(msg, msg2, &timeouts, link) - handle_msg_timeout(msg); - - kref_put(&intf->refcount, intf_free); - spin_lock(&interfaces_lock); + deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); + + /* + * Maintenance mode handling. Check the timeout + * optimistically before we claim the lock. It may + * mean a timeout gets missed occasionally, but that + * only means the timeout gets extended by one period + * in that case. No big deal, and it avoids the lock + * most of the time. + */ + if (intf->auto_maintenance_timeout > 0) { + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + if (intf->auto_maintenance_timeout > 0) { + intf->auto_maintenance_timeout + -= timeout_period; + if (!intf->maintenance_mode + && (intf->auto_maintenance_timeout <= 0)) { + intf->maintenance_mode_enable = 0; + maintenance_mode_update(intf); + } + } + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); + } } - spin_unlock(&interfaces_lock); + rcu_read_unlock(); } static void ipmi_request_event(void) { - ipmi_smi_t intf; - int i; + ipmi_smi_t intf; + struct ipmi_smi_handlers *handlers; - spin_lock(&interfaces_lock); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + rcu_read_lock(); + /* + * Called from the timer, no need to check if handlers is + * valid. + */ + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + /* No event requests when in maintenance mode. */ + if (intf->maintenance_mode_enable) continue; - intf->handlers->request_events(intf->send_info); + handlers = intf->handlers; + if (handlers) + handlers->request_events(intf->send_info); } - spin_unlock(&interfaces_lock); + rcu_read_unlock(); } static struct timer_list ipmi_timer; @@ -2853,10 +4031,12 @@ static struct timer_list ipmi_timer; /* How many jiffies does it take to get to the timeout time. */ #define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) -/* Request events from the queue every second (this is the number of - IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the - future, IPMI will add a way to know immediately if an event is in - the queue and this silliness can go away. */ +/* + * Request events from the queue every second (this is the number of + * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the + * future, IPMI will add a way to know immediately if an event is in + * the queue and this silliness can go away. + */ #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) static atomic_t stop_operation; @@ -2900,6 +4080,7 @@ struct ipmi_smi_msg *ipmi_alloc_smi_msg(void) } return rv; } +EXPORT_SYMBOL(ipmi_alloc_smi_msg); static void free_recv_msg(struct ipmi_recv_msg *msg) { @@ -2907,12 +4088,13 @@ static void free_recv_msg(struct ipmi_recv_msg *msg) kfree(msg); } -struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) +static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) { struct ipmi_recv_msg *rv; rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); if (rv) { + rv->user = NULL; rv->done = free_recv_msg; atomic_inc(&recv_msg_inuse_count); } @@ -2925,6 +4107,7 @@ void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) kref_put(&msg->user->refcount, free_user); msg->done(msg); } +EXPORT_SYMBOL(ipmi_free_recv_msg); #ifdef CONFIG_IPMI_PANIC_EVENT @@ -2942,8 +4125,7 @@ static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD) - && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) - { + && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { /* A get event receiver command, save it. */ intf->event_receiver = msg->msg.data[1]; intf->event_receiver_lun = msg->msg.data[2] & 0x3; @@ -2955,10 +4137,11 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD) - && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) - { - /* A get device id command, save if we are an event - receiver or generator. */ + && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { + /* + * A get device id command, save if we are an event + * receiver or generator. + */ intf->local_sel_device = (msg->msg.data[6] >> 2) & 1; intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; } @@ -2970,7 +4153,6 @@ static void send_panic_events(char *str) struct kernel_ipmi_msg msg; ipmi_smi_t intf; unsigned char data[16]; - int i; struct ipmi_system_interface_addr *si; struct ipmi_addr addr; struct ipmi_smi_msg smi_msg; @@ -2986,14 +4168,16 @@ static void send_panic_events(char *str) msg.cmd = 2; /* Platform event command. */ msg.data = data; msg.data_len = 8; - data[0] = 0x21; /* Kernel generator ID, IPMI table 5-4 */ + data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */ data[1] = 0x03; /* This is for IPMI 1.0. */ data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */ data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ - /* Put a few breadcrumbs in. Hopefully later we can add more things - to make the panic events more useful. */ + /* + * Put a few breadcrumbs in. Hopefully later we can add more things + * to make the panic events more useful. + */ if (str) { data[3] = str[0]; data[6] = str[1]; @@ -3004,11 +4188,12 @@ static void send_panic_events(char *str) recv_msg.done = dummy_recv_done_handler; /* For every registered interface, send the event. */ - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (!intf->handlers) + /* Interface is not ready. */ continue; + intf->run_to_completion = 1; /* Send the event announcing the panic. */ intf->handlers->set_run_to_completion(intf->send_info, 1); i_ipmi_request(NULL, @@ -3026,25 +4211,38 @@ static void send_panic_events(char *str) } #ifdef CONFIG_IPMI_PANIC_STRING - /* On every interface, dump a bunch of OEM event holding the - string. */ - if (!str) + /* + * On every interface, dump a bunch of OEM event holding the + * string. + */ + if (!str) return; - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { + /* For every registered interface, send the event. */ + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { char *p = str; struct ipmi_ipmb_addr *ipmb; int j; - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + if (intf->intf_num == -1) + /* Interface was not ready yet. */ continue; - /* First job here is to figure out where to send the - OEM events. There's no way in IPMI to send OEM - events using an event send command, so we have to - find the SEL to put them in and stick them in - there. */ + /* + * intf_num is used as an marker to tell if the + * interface is valid. Thus we need a read barrier to + * make sure data fetched before checking intf_num + * won't be used. + */ + smp_rmb(); + + /* + * First job here is to figure out where to send the + * OEM events. There's no way in IPMI to send OEM + * events using an event send command, so we have to + * find the SEL to put them in and stick them in + * there. + */ /* Get capabilities from the get device id. */ intf->local_sel_device = 0; @@ -3092,24 +4290,29 @@ static void send_panic_events(char *str) } intf->null_user_handler = NULL; - /* Validate the event receiver. The low bit must not - be 1 (it must be a valid IPMB address), it cannot - be zero, and it must not be my address. */ - if (((intf->event_receiver & 1) == 0) + /* + * Validate the event receiver. The low bit must not + * be 1 (it must be a valid IPMB address), it cannot + * be zero, and it must not be my address. + */ + if (((intf->event_receiver & 1) == 0) && (intf->event_receiver != 0) - && (intf->event_receiver != intf->channels[0].address)) - { - /* The event receiver is valid, send an IPMB - message. */ + && (intf->event_receiver != intf->channels[0].address)) { + /* + * The event receiver is valid, send an IPMB + * message. + */ ipmb = (struct ipmi_ipmb_addr *) &addr; ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; ipmb->channel = 0; /* FIXME - is this right? */ ipmb->lun = intf->event_receiver_lun; ipmb->slave_addr = intf->event_receiver; } else if (intf->local_sel_device) { - /* The event receiver was not valid (or was - me), but I am an SEL device, just dump it - in my SEL. */ + /* + * The event receiver was not valid (or was + * me), but I am an SEL device, just dump it + * in my SEL. + */ si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = IPMI_BMC_CHANNEL; @@ -3117,7 +4320,6 @@ static void send_panic_events(char *str) } else continue; /* No where to send the event. */ - msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; msg.data = data; @@ -3134,8 +4336,10 @@ static void send_panic_events(char *str) data[2] = 0xf0; /* OEM event without timestamp. */ data[3] = intf->channels[0].address; data[4] = j++; /* sequence # */ - /* Always give 11 bytes, so strncpy will fill - it with zeroes for me. */ + /* + * Always give 11 bytes, so strncpy will fill + * it with zeroes for me. + */ strncpy(data+5, p, 11); p += size; @@ -3152,30 +4356,30 @@ static void send_panic_events(char *str) intf->channels[0].lun, 0, 1); /* no retry, and no wait. */ } - } + } #endif /* CONFIG_IPMI_PANIC_STRING */ } #endif /* CONFIG_IPMI_PANIC_EVENT */ -static int has_paniced = 0; +static int has_panicked; static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - int i; ipmi_smi_t intf; - if (has_paniced) + if (has_panicked) return NOTIFY_DONE; - has_paniced = 1; + has_panicked = 1; /* For every registered interface, set it to run to completion. */ - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (!intf->handlers) + /* Interface is not ready. */ continue; + intf->run_to_completion = 1; intf->handlers->set_run_to_completion(intf->send_info, 1); } @@ -3194,17 +4398,20 @@ static struct notifier_block panic_block = { static int ipmi_init_msghandler(void) { - int i; + int rv; if (initialized) return 0; + rv = driver_register(&ipmidriver.driver); + if (rv) { + printk(KERN_ERR PFX "Could not register IPMI driver\n"); + return rv; + } + printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) - ipmi_interfaces[i] = NULL; - #ifdef CONFIG_PROC_FS proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { @@ -3212,16 +4419,12 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } - proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ - init_timer(&ipmi_timer); - ipmi_timer.data = 0; - ipmi_timer.function = ipmi_timeout; - ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES; - add_timer(&ipmi_timer); + setup_timer(&ipmi_timer, ipmi_timeout, 0); + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); - notifier_chain_register(&panic_notifier_list, &panic_block); + atomic_notifier_chain_register(&panic_notifier_list, &panic_block); initialized = 1; @@ -3241,20 +4444,27 @@ static __exit void cleanup_ipmi(void) if (!initialized) return; - notifier_chain_unregister(&panic_notifier_list, &panic_block); + atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); - /* This can't be called if any interfaces exist, so no worry about - shutting down the interfaces. */ + /* + * This can't be called if any interfaces exist, so no worry + * about shutting down the interfaces. + */ - /* Tell the timer to stop, then wait for it to stop. This avoids - problems with race conditions removing the timer here. */ + /* + * Tell the timer to stop, then wait for it to stop. This + * avoids problems with race conditions removing the timer + * here. + */ atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); #ifdef CONFIG_PROC_FS - remove_proc_entry(proc_ipmi_root->name, &proc_root); + remove_proc_entry(proc_ipmi_root->name, NULL); #endif /* CONFIG_PROC_FS */ + driver_unregister(&ipmidriver.driver); + initialized = 0; /* Check for buffer leaks. */ @@ -3272,31 +4482,6 @@ module_exit(cleanup_ipmi); module_init(ipmi_init_msghandler_mod); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard "); -MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI interface."); +MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI" + " interface."); MODULE_VERSION(IPMI_DRIVER_VERSION); - -EXPORT_SYMBOL(ipmi_create_user); -EXPORT_SYMBOL(ipmi_destroy_user); -EXPORT_SYMBOL(ipmi_get_version); -EXPORT_SYMBOL(ipmi_request_settime); -EXPORT_SYMBOL(ipmi_request_supply_msgs); -EXPORT_SYMBOL(ipmi_register_smi); -EXPORT_SYMBOL(ipmi_unregister_smi); -EXPORT_SYMBOL(ipmi_register_for_cmd); -EXPORT_SYMBOL(ipmi_unregister_for_cmd); -EXPORT_SYMBOL(ipmi_smi_msg_received); -EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); -EXPORT_SYMBOL(ipmi_alloc_smi_msg); -EXPORT_SYMBOL(ipmi_addr_length); -EXPORT_SYMBOL(ipmi_validate_addr); -EXPORT_SYMBOL(ipmi_set_gets_events); -EXPORT_SYMBOL(ipmi_smi_watcher_register); -EXPORT_SYMBOL(ipmi_smi_watcher_unregister); -EXPORT_SYMBOL(ipmi_set_my_address); -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); -EXPORT_SYMBOL(ipmi_free_recv_msg);