From c4edff1c19ef23e15aae64ca03f32c6719822d54 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 7 Nov 2005 00:59:56 -0800 Subject: [PATCH] ipmi: various si cleanup A number of small changes for the various system interface drivers, consolidated from a number of patches from Matt Domsch. Clear B2H_ATN and drain the BMC message buffer on command timeout. This prevents further commands from failing after a timeout. Add bt_debug and smic_debug module parameters, expose them in sysfs. This lets you enable and disable debugging messages at runtime. Unsigned jiffies math in ipmi_si_intf.c causes a too-large value to be passed to ->event() after jiffies wrap-around. The BT driver had caught this, but didn't know how to fix it. Now all calls to ->event() use a sane value for time. Increase timeout for commands handed to the BT driver from 2 seconds to 5 seconds. This is necessary particularly when the previous command was a "Clear SEL", as that command completes, yet the BMC isn't really ready to handle another command yet. Silence BT debugging messages which were being printed on the console. Increase SMIC timeout form 1/10s to 2s. This is needed on Dell PowerEdge 2650 and PowerEdge 750 with ERA/O cards to allow commands to complete without timing out. Adds kcs_debug module param, to match behavior of BT and SMIC. This also prevents messages from being sent to the console unless explicitly requested. Signed-off-by: Matt Domsch Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index b6e5cbfb09f8..204e2e987e90 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -819,7 +819,7 @@ static void smi_timeout(unsigned long data) enum si_sm_result smi_result; unsigned long flags; unsigned long jiffies_now; - unsigned long time_diff; + long time_diff; #ifdef DEBUG_TIMING struct timeval t; #endif @@ -835,7 +835,7 @@ static void smi_timeout(unsigned long data) printk("**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif jiffies_now = jiffies; - time_diff = ((jiffies_now - smi_info->last_timeout_jiffies) + time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies) * SI_USEC_PER_JIFFY); smi_result = smi_event_handler(smi_info, time_diff); -- cgit v1.2.3 From d5a2b89a4943b423b5b0a07783fee4e08424b0b2 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 7 Nov 2005 00:59:58 -0800 Subject: [PATCH] ipmi: more dell fixes Make SMIC driver ignore EVT_AVAIL and SMS_ATN bits in flags register, as they're used by systems management interrupts, not the host OS. Make the OEM0 Data Available handler work for pre-IPMI 1.5 systems from Dell too. Without these two fixes, PowerEdge 2650 and other similar systems with SMIC may hang a process (modprobe or anything using /dev/ipmi0). Signed-off-by: Matt Domsch Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 23 ++++++++++++++++------- drivers/char/ipmi/ipmi_smic_sm.c | 9 +++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 204e2e987e90..df7dbbff57ae 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2052,6 +2052,9 @@ static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) * IPMI Version = 0x51 IPMI 1.5 * Manufacturer ID = A2 02 00 Dell IANA * + * Additionally, PowerEdge systems with IPMI < 1.5 may also assert + * OEM0_DATA_AVAIL and needs to be treated as RECEIVE_MSG_AVAIL. + * */ #define DELL_POWEREDGE_8G_BMC_DEVICE_ID 0x20 #define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80 @@ -2061,13 +2064,19 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; const char mfr[3]=DELL_IANA_MFR_ID; - if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) - && (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID) - && (id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV) - && (id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION)) - { - smi_info->oem_data_avail_handler = - oem_data_avail_to_receive_msg_avail; + if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr))) { + if (id->device_id == DELL_POWEREDGE_8G_BMC_DEVICE_ID && + id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV && + id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { + smi_info->oem_data_avail_handler = + oem_data_avail_to_receive_msg_avail; + } + else if (ipmi_version_major(id) < 1 || + (ipmi_version_major(id) == 1 && + ipmi_version_minor(id) < 5)) { + smi_info->oem_data_avail_handler = + oem_data_avail_to_receive_msg_avail; + } } } diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c index f17043da9dd5..39d7e5ef1a2b 100644 --- a/drivers/char/ipmi/ipmi_smic_sm.c +++ b/drivers/char/ipmi/ipmi_smic_sm.c @@ -85,6 +85,12 @@ enum smic_states { /* SMIC Flags Register Bits */ #define SMIC_RX_DATA_READY 0x80 #define SMIC_TX_DATA_READY 0x40 +/* + * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by + * a few systems, and then only by Systems Management + * Interrupts, not by the OS. Always ignore these bits. + * + */ #define SMIC_SMI 0x10 #define SMIC_EVM_DATA_AVAIL 0x08 #define SMIC_SMS_DATA_AVAIL 0x04 @@ -368,8 +374,7 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) switch (smic->state) { case SMIC_IDLE: /* in IDLE we check for available messages */ - if (flags & (SMIC_SMI | - SMIC_EVM_DATA_AVAIL | SMIC_SMS_DATA_AVAIL)) + if (flags & SMIC_SMS_DATA_AVAIL) { return SI_SM_ATTN; } -- cgit v1.2.3 From ea94027b92dd0d02d238d5984cd9089343c1d6cc Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 7 Nov 2005 00:59:59 -0800 Subject: [PATCH] ipmi: si start transaction hook Some commands, on some system BMCs, don't respond at at all. This is seen on Dell PowerEdge x6xx and x7xx systems with IPMI 1.0 BT controllers when a "Get SDR" command is issued, with a length field of 0x3A, which happens to be the length of about SDR entries. If another length is passed, this command succeeds. This patch adds general infrastructure for receiving commands before they're passed down to the low-level drivers, such that they can be completed immediately, or modified, prior to being sent to ->start_transaction(). Signed-off-by: Matt Domsch Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index df7dbbff57ae..2ace62b1d326 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #ifdef CONFIG_HIGH_RES_TIMERS #include @@ -222,6 +223,12 @@ struct smi_info unsigned long incoming_messages; }; +static struct notifier_block *xaction_notifier_list; +static int register_xaction_notifier(struct notifier_block * nb) +{ + return notifier_chain_register(&xaction_notifier_list, nb); +} + static void si_restart_short_timer(struct smi_info *smi_info); static void deliver_recv_msg(struct smi_info *smi_info, @@ -281,6 +288,11 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) do_gettimeofday(&t); printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif + err = notifier_call_chain(&xaction_notifier_list, 0, smi_info); + if (err & NOTIFY_STOP_MASK) { + rv = SI_SM_CALL_WITHOUT_DELAY; + goto out; + } err = smi_info->handlers->start_transaction( smi_info->si_sm, smi_info->curr_msg->data, @@ -291,6 +303,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) rv = SI_SM_CALL_WITHOUT_DELAY; } + out: spin_unlock(&(smi_info->msg_lock)); return rv; @@ -2080,6 +2093,71 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) } } +#define CANNOT_RETURN_REQUESTED_LENGTH 0xCA +static void return_hosed_msg_badsize(struct smi_info *smi_info) +{ + struct ipmi_smi_msg *msg = smi_info->curr_msg; + + /* Make it a reponse */ + msg->rsp[0] = msg->data[0] | 4; + msg->rsp[1] = msg->data[1]; + msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH; + msg->rsp_size = 3; + smi_info->curr_msg = NULL; + deliver_recv_msg(smi_info, msg); +} + +/* + * dell_poweredge_bt_xaction_handler + * @info - smi_info.device_id must be populated + * + * Dell PowerEdge servers with the BT interface (x6xx and 1750) will + * not respond to a Get SDR command if the length of the data + * requested is exactly 0x3A, which leads to command timeouts and no + * data returned. This intercepts such commands, and causes userspace + * callers to try again with a different-sized buffer, which succeeds. + */ + +#define STORAGE_NETFN 0x0A +#define STORAGE_CMD_GET_SDR 0x23 +static int dell_poweredge_bt_xaction_handler(struct notifier_block *self, + unsigned long unused, + void *in) +{ + struct smi_info *smi_info = in; + unsigned char *data = smi_info->curr_msg->data; + unsigned int size = smi_info->curr_msg->data_size; + if (size >= 8 && + (data[0]>>2) == STORAGE_NETFN && + data[1] == STORAGE_CMD_GET_SDR && + data[7] == 0x3A) { + return_hosed_msg_badsize(smi_info); + return NOTIFY_STOP; + } + return NOTIFY_DONE; +} + +static struct notifier_block dell_poweredge_bt_xaction_notifier = { + .notifier_call = dell_poweredge_bt_xaction_handler, +}; + +/* + * setup_dell_poweredge_bt_xaction_handler + * @info - smi_info.device_id must be filled in already + * + * Fills in smi_info.device_id.start_transaction_pre_hook + * when we know what function to use there. + */ +static void +setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) +{ + struct ipmi_device_id *id = &smi_info->device_id; + const char mfr[3]=DELL_IANA_MFR_ID; + if (! memcmp(mfr, id->manufacturer_id, sizeof(mfr)) && + smi_info->si_type == SI_BT) + register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); +} + /* * setup_oem_data_handler * @info - smi_info.device_id must be filled in already @@ -2093,6 +2171,11 @@ static void setup_oem_data_handler(struct smi_info *smi_info) setup_dell_poweredge_oem_data_handler(smi_info); } +static void setup_xaction_handlers(struct smi_info *smi_info) +{ + setup_dell_poweredge_bt_xaction_handler(smi_info); +} + /* Returns 0 if initialized, or negative on an error. */ static int init_one_smi(int intf_num, struct smi_info **smi) { @@ -2188,6 +2271,7 @@ static int init_one_smi(int intf_num, struct smi_info **smi) goto out_err; setup_oem_data_handler(new_smi); + setup_xaction_handlers(new_smi); /* Try to claim any interrupts. */ new_smi->irq_setup(new_smi); -- cgit v1.2.3 From c3e7e7916ec61cf58c88af12f4db17f28cffd83a Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 7 Nov 2005 01:00:02 -0800 Subject: [PATCH] ipmi: kcs error0 delay BMCs can get into ERROR0 state while flashing new firmware, particularly while the BMC is erasing the next flash block, which may take a just under 2 seconds on a Dell PowerEdge 2800 (1.75 seconds typical), during which time the single-threaded firmware may not be able to process new commands. In particular, clearing OBF may not take effect immediately. We want it to delay in ERROR0 after clearing OBF a bit waiting for OBF to actually be clear before proceeding. This introduces a new return value from the LLDD's event loop, SI_SM_CALL_WITH_TICK_DELAY. This means the calling thread/timer should schedule_timeout() at least 1 tick, rather than busy-wait. This is a longer delay than SI_SM_CALL_WITH_DELAY, which is typically a 250us busy-wait. Signed-off-by: Matt Domsch Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_kcs_sm.c | 8 ++++++++ drivers/char/ipmi/ipmi_si_intf.c | 3 ++- drivers/char/ipmi/ipmi_si_sm.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index dc83365ede4a..da1554194d3d 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -41,6 +41,7 @@ #include #include #include +#include #include /* for completion codes */ #include "ipmi_si_sm.h" @@ -99,6 +100,7 @@ enum kcs_states { #define IBF_RETRY_TIMEOUT 1000000 #define OBF_RETRY_TIMEOUT 1000000 #define MAX_ERROR_RETRIES 10 +#define ERROR0_OBF_WAIT_JIFFIES (2*HZ) struct si_sm_data { @@ -115,6 +117,7 @@ struct si_sm_data unsigned int error_retries; long ibf_timeout; long obf_timeout; + unsigned long error0_timeout; }; static unsigned int init_kcs_data(struct si_sm_data *kcs, @@ -187,6 +190,7 @@ static inline void start_error_recovery(struct si_sm_data *kcs, char *reason) printk(KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n", reason); kcs->state = KCS_HOSED; } else { + kcs->error0_timeout = jiffies + ERROR0_OBF_WAIT_JIFFIES; kcs->state = KCS_ERROR0; } } @@ -423,6 +427,10 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) case KCS_ERROR0: clear_obf(kcs, status); + status = read_status(kcs); + if (GET_STATUS_OBF(status)) /* controller isn't responding */ + if (time_before(jiffies, kcs->error0_timeout)) + return SI_SM_CALL_WITH_TICK_DELAY; write_cmd(kcs, KCS_GET_STATUS_ABORT); kcs->state = KCS_ERROR1; break; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 2ace62b1d326..d514df7c7283 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1932,7 +1932,8 @@ static int try_get_dev_id(struct smi_info *smi_info) smi_result = smi_info->handlers->event(smi_info->si_sm, 0); for (;;) { - if (smi_result == SI_SM_CALL_WITH_DELAY) { + if (smi_result == SI_SM_CALL_WITH_DELAY || + smi_result == SI_SM_CALL_WITH_TICK_DELAY) { schedule_timeout_uninterruptible(1); smi_result = smi_info->handlers->event( smi_info->si_sm, 100); diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index 62791dd42985..bf3d4962d6a5 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -62,6 +62,7 @@ enum si_sm_result { SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */ SI_SM_CALL_WITH_DELAY, /* Delay some before calling again. */ + SI_SM_CALL_WITH_TICK_DELAY, /* Delay at least 1 tick before calling again. */ SI_SM_TRANSACTION_COMPLETE, /* A transaction is finished. */ SI_SM_IDLE, /* The SM is in idle state. */ SI_SM_HOSED, /* The hardware violated the state machine. */ -- cgit v1.2.3 From a9a2c44ff0a1350f8bfe3a162ecf71b1c9ce5cc2 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 7 Nov 2005 01:00:03 -0800 Subject: [PATCH] ipmi: add timer thread We must poll for responses to commands when interrupts aren't in use. The default poll interval is based on using a kernel timer, which varies with HZ. For character-based interfaces like KCS and SMIC though, that can be way too slow (>15 minutes to flash a new firmware with KCS, >20 seconds to retrieve the sensor list). This creates a low-priority kernel thread to poll more often. If the state machine is idle, so is the kernel thread. But if there's an active command, it polls quite rapidly. This decrease a firmware flash time from 15 minutes to 1.5 minutes, and the sensor list time to 4.5 seconds, on a Dell PowerEdge x8x system. The timer-based polling remains, to ensure some amount of responsiveness even under high user process CPU load. Checking for a stopped timer at rmmod now uses atomics and del_timer_sync() to ensure safe stoppage. Signed-off-by: Matt Domsch Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 84 ++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 21 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index d514df7c7283..fa3be622ca97 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -126,6 +126,7 @@ struct ipmi_device_id { struct smi_info { + int intf_num; ipmi_smi_t intf; struct si_sm_data *si_sm; struct si_sm_handlers *handlers; @@ -193,8 +194,7 @@ struct smi_info unsigned long last_timeout_jiffies; /* Used to gracefully stop the timer without race conditions. */ - volatile int stop_operation; - volatile int timer_stopped; + atomic_t stop_operation; /* The driver will disable interrupts when it gets into a situation where it cannot handle messages due to lack of @@ -221,6 +221,9 @@ struct smi_info unsigned long events; unsigned long watchdog_pretimeouts; unsigned long incoming_messages; + + struct completion exiting; + long thread_pid; }; static struct notifier_block *xaction_notifier_list; @@ -779,6 +782,38 @@ static void set_run_to_completion(void *send_info, int i_run_to_completion) spin_unlock_irqrestore(&(smi_info->si_lock), flags); } +static int ipmi_thread(void *data) +{ + struct smi_info *smi_info = data; + unsigned long flags, last=1; + enum si_sm_result smi_result; + + daemonize("kipmi%d", smi_info->intf_num); + allow_signal(SIGKILL); + set_user_nice(current, 19); + while (!atomic_read(&smi_info->stop_operation)) { + schedule_timeout(last); + spin_lock_irqsave(&(smi_info->si_lock), flags); + smi_result=smi_event_handler(smi_info, 0); + spin_unlock_irqrestore(&(smi_info->si_lock), flags); + if (smi_result == SI_SM_CALL_WITHOUT_DELAY) + last = 0; + else if (smi_result == SI_SM_CALL_WITH_DELAY) { + udelay(1); + last = 0; + } + else { + /* System is idle; go to sleep */ + last = 1; + current->state = TASK_INTERRUPTIBLE; + } + } + smi_info->thread_pid = 0; + complete_and_exit(&(smi_info->exiting), 0); + return 0; +} + + static void poll(void *send_info) { struct smi_info *smi_info = send_info; @@ -837,10 +872,8 @@ static void smi_timeout(unsigned long data) struct timeval t; #endif - if (smi_info->stop_operation) { - smi_info->timer_stopped = 1; + if (atomic_read(&smi_info->stop_operation)) return; - } spin_lock_irqsave(&(smi_info->si_lock), flags); #ifdef DEBUG_TIMING @@ -913,7 +946,7 @@ static irqreturn_t si_irq_handler(int irq, void *data, struct pt_regs *regs) smi_info->interrupts++; spin_unlock(&smi_info->count_lock); - if (smi_info->stop_operation) + if (atomic_read(&smi_info->stop_operation)) goto out; #ifdef DEBUG_TIMING @@ -1432,7 +1465,7 @@ static u32 ipmi_acpi_gpe(void *context) smi_info->interrupts++; spin_unlock(&smi_info->count_lock); - if (smi_info->stop_operation) + if (atomic_read(&smi_info->stop_operation)) goto out; #ifdef DEBUG_TIMING @@ -2177,6 +2210,16 @@ static void setup_xaction_handlers(struct smi_info *smi_info) setup_dell_poweredge_bt_xaction_handler(smi_info); } +static inline void wait_for_timer_and_thread(struct smi_info *smi_info) +{ + if (smi_info->thread_pid > 0) { + /* wake the potentially sleeping thread */ + kill_proc(smi_info->thread_pid, SIGKILL, 0); + wait_for_completion(&(smi_info->exiting)); + } + del_timer_sync(&smi_info->si_timer); +} + /* Returns 0 if initialized, or negative on an error. */ static int init_one_smi(int intf_num, struct smi_info **smi) { @@ -2284,8 +2327,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) new_smi->run_to_completion = 0; new_smi->interrupt_disabled = 0; - new_smi->timer_stopped = 0; - new_smi->stop_operation = 0; + atomic_set(&new_smi->stop_operation, 0); + new_smi->intf_num = intf_num; /* Start clearing the flags before we enable interrupts or the timer to avoid racing with the timer. */ @@ -2303,7 +2346,14 @@ static int init_one_smi(int intf_num, struct smi_info **smi) new_smi->si_timer.function = smi_timeout; new_smi->last_timeout_jiffies = jiffies; new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; + add_timer(&(new_smi->si_timer)); + if (new_smi->si_type != SI_BT) { + init_completion(&(new_smi->exiting)); + new_smi->thread_pid = kernel_thread(ipmi_thread, new_smi, + CLONE_FS|CLONE_FILES| + CLONE_SIGHAND); + } rv = ipmi_register_smi(&handlers, new_smi, @@ -2345,12 +2395,8 @@ static int init_one_smi(int intf_num, struct smi_info **smi) return 0; out_err_stop_timer: - new_smi->stop_operation = 1; - - /* Wait for the timer to stop. This avoids problems with race - conditions removing the timer here. */ - while (!new_smi->timer_stopped) - schedule_timeout_uninterruptible(1); + atomic_inc(&new_smi->stop_operation); + wait_for_timer_and_thread(new_smi); out_err: if (new_smi->intf) @@ -2456,8 +2502,7 @@ static void __exit cleanup_one_si(struct smi_info *to_clean) spin_lock_irqsave(&(to_clean->si_lock), flags); spin_lock(&(to_clean->msg_lock)); - to_clean->stop_operation = 1; - + atomic_inc(&to_clean->stop_operation); to_clean->irq_cleanup(to_clean); spin_unlock(&(to_clean->msg_lock)); @@ -2468,10 +2513,7 @@ static void __exit cleanup_one_si(struct smi_info *to_clean) interrupt. */ synchronize_sched(); - /* Wait for the timer to stop. This avoids problems with race - conditions removing the timer here. */ - while (!to_clean->timer_stopped) - schedule_timeout_uninterruptible(1); + wait_for_timer_and_thread(to_clean); /* Interrupts and timeouts are stopped, now make sure the interface is in a clean state. */ -- cgit v1.2.3 From e9a705a0a0ed99833cfef40d509f63a052638f00 Mon Sep 17 00:00:00 2001 From: Matt Domsch Date: Mon, 7 Nov 2005 01:00:04 -0800 Subject: [PATCH] ipmi: use kthread API Convert ipmi driver thread to kthread API, only sleep when interface is idle. Signed-off-by: Matt Domsch Cc: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_si_intf.c | 45 ++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 30 deletions(-) (limited to 'drivers/char/ipmi/ipmi_si_intf.c') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index fa3be622ca97..ea89dca3dbb5 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #ifdef CONFIG_HIGH_RES_TIMERS #include @@ -222,8 +223,7 @@ struct smi_info unsigned long watchdog_pretimeouts; unsigned long incoming_messages; - struct completion exiting; - long thread_pid; + struct task_struct *thread; }; static struct notifier_block *xaction_notifier_list; @@ -785,31 +785,22 @@ static void set_run_to_completion(void *send_info, int i_run_to_completion) static int ipmi_thread(void *data) { struct smi_info *smi_info = data; - unsigned long flags, last=1; + unsigned long flags; enum si_sm_result smi_result; - daemonize("kipmi%d", smi_info->intf_num); - allow_signal(SIGKILL); set_user_nice(current, 19); - while (!atomic_read(&smi_info->stop_operation)) { - schedule_timeout(last); + while (!kthread_should_stop()) { spin_lock_irqsave(&(smi_info->si_lock), flags); smi_result=smi_event_handler(smi_info, 0); spin_unlock_irqrestore(&(smi_info->si_lock), flags); - if (smi_result == SI_SM_CALL_WITHOUT_DELAY) - last = 0; - else if (smi_result == SI_SM_CALL_WITH_DELAY) { - udelay(1); - last = 0; - } - else { - /* System is idle; go to sleep */ - last = 1; - current->state = TASK_INTERRUPTIBLE; + if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { + /* do nothing */ } + else if (smi_result == SI_SM_CALL_WITH_DELAY) + udelay(1); + else + schedule_timeout_interruptible(1); } - smi_info->thread_pid = 0; - complete_and_exit(&(smi_info->exiting), 0); return 0; } @@ -2212,11 +2203,8 @@ static void setup_xaction_handlers(struct smi_info *smi_info) static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { - if (smi_info->thread_pid > 0) { - /* wake the potentially sleeping thread */ - kill_proc(smi_info->thread_pid, SIGKILL, 0); - wait_for_completion(&(smi_info->exiting)); - } + if (smi_info->thread != ERR_PTR(-ENOMEM)) + kthread_stop(smi_info->thread); del_timer_sync(&smi_info->si_timer); } @@ -2348,12 +2336,9 @@ static int init_one_smi(int intf_num, struct smi_info **smi) new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; add_timer(&(new_smi->si_timer)); - if (new_smi->si_type != SI_BT) { - init_completion(&(new_smi->exiting)); - new_smi->thread_pid = kernel_thread(ipmi_thread, new_smi, - CLONE_FS|CLONE_FILES| - CLONE_SIGHAND); - } + if (new_smi->si_type != SI_BT) + new_smi->thread = kthread_run(ipmi_thread, new_smi, + "kipmi%d", new_smi->intf_num); rv = ipmi_register_smi(&handlers, new_smi, -- cgit v1.2.3