diff options
author | Corey Minyard <minyard@acm.org> | 2007-05-08 03:23:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:14:58 -0400 |
commit | ee6cd5f8f573ad11f270a07fb201822c2862474d (patch) | |
tree | 2daf7e039fc3ea304223addf2bc27678fc77487c /drivers/char | |
parent | dba9b4f6a096f39dd58d67fbc643a7c1bf2973eb (diff) |
ipmi: allow shared interrupts
The IPMI driver used enable_irq and disable_irq when it got into situations
where it couldn't allocate memory; it did this to avoid having the interrupt
just lock the machine when it couldn't get memory to perform the transaction
to disable the interrupt.
This patch modifies the driver to not use disable_irq and enable_irq. It
instead sends the messages to the BMC to perform this operation. It also
makes sure interrupts are cleanly disabled when the interface is shut down and
cleans up some shutdown things that are no longer necessary.
Signed-off-by: Corey Minyard <cminyard@mvista.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 124 |
1 files changed, 89 insertions, 35 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index e117049fb754..a323cc7a0265 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -82,6 +82,12 @@ | |||
82 | #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a | 82 | #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a |
83 | short timeout */ | 83 | short timeout */ |
84 | 84 | ||
85 | /* Bit for BMC global enables. */ | ||
86 | #define IPMI_BMC_RCV_MSG_INTR 0x01 | ||
87 | #define IPMI_BMC_EVT_MSG_INTR 0x02 | ||
88 | #define IPMI_BMC_EVT_MSG_BUFF 0x04 | ||
89 | #define IPMI_BMC_SYS_LOG 0x08 | ||
90 | |||
85 | enum si_intf_state { | 91 | enum si_intf_state { |
86 | SI_NORMAL, | 92 | SI_NORMAL, |
87 | SI_GETTING_FLAGS, | 93 | SI_GETTING_FLAGS, |
@@ -90,7 +96,9 @@ enum si_intf_state { | |||
90 | SI_CLEARING_FLAGS_THEN_SET_IRQ, | 96 | SI_CLEARING_FLAGS_THEN_SET_IRQ, |
91 | SI_GETTING_MESSAGES, | 97 | SI_GETTING_MESSAGES, |
92 | SI_ENABLE_INTERRUPTS1, | 98 | SI_ENABLE_INTERRUPTS1, |
93 | SI_ENABLE_INTERRUPTS2 | 99 | SI_ENABLE_INTERRUPTS2, |
100 | SI_DISABLE_INTERRUPTS1, | ||
101 | SI_DISABLE_INTERRUPTS2 | ||
94 | /* FIXME - add watchdog stuff. */ | 102 | /* FIXME - add watchdog stuff. */ |
95 | }; | 103 | }; |
96 | 104 | ||
@@ -339,6 +347,17 @@ static void start_enable_irq(struct smi_info *smi_info) | |||
339 | smi_info->si_state = SI_ENABLE_INTERRUPTS1; | 347 | smi_info->si_state = SI_ENABLE_INTERRUPTS1; |
340 | } | 348 | } |
341 | 349 | ||
350 | static void start_disable_irq(struct smi_info *smi_info) | ||
351 | { | ||
352 | unsigned char msg[2]; | ||
353 | |||
354 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | ||
355 | msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; | ||
356 | |||
357 | smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); | ||
358 | smi_info->si_state = SI_DISABLE_INTERRUPTS1; | ||
359 | } | ||
360 | |||
342 | static void start_clear_flags(struct smi_info *smi_info) | 361 | static void start_clear_flags(struct smi_info *smi_info) |
343 | { | 362 | { |
344 | unsigned char msg[3]; | 363 | unsigned char msg[3]; |
@@ -359,7 +378,7 @@ static void start_clear_flags(struct smi_info *smi_info) | |||
359 | static inline void disable_si_irq(struct smi_info *smi_info) | 378 | static inline void disable_si_irq(struct smi_info *smi_info) |
360 | { | 379 | { |
361 | if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { | 380 | if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { |
362 | disable_irq_nosync(smi_info->irq); | 381 | start_disable_irq(smi_info); |
363 | smi_info->interrupt_disabled = 1; | 382 | smi_info->interrupt_disabled = 1; |
364 | } | 383 | } |
365 | } | 384 | } |
@@ -367,7 +386,7 @@ static inline void disable_si_irq(struct smi_info *smi_info) | |||
367 | static inline void enable_si_irq(struct smi_info *smi_info) | 386 | static inline void enable_si_irq(struct smi_info *smi_info) |
368 | { | 387 | { |
369 | if ((smi_info->irq) && (smi_info->interrupt_disabled)) { | 388 | if ((smi_info->irq) && (smi_info->interrupt_disabled)) { |
370 | enable_irq(smi_info->irq); | 389 | start_enable_irq(smi_info); |
371 | smi_info->interrupt_disabled = 0; | 390 | smi_info->interrupt_disabled = 0; |
372 | } | 391 | } |
373 | } | 392 | } |
@@ -589,7 +608,9 @@ static void handle_transaction_done(struct smi_info *smi_info) | |||
589 | } else { | 608 | } else { |
590 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | 609 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); |
591 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; | 610 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; |
592 | msg[2] = msg[3] | 1; /* enable msg queue int */ | 611 | msg[2] = (msg[3] | |
612 | IPMI_BMC_RCV_MSG_INTR | | ||
613 | IPMI_BMC_EVT_MSG_INTR); | ||
593 | smi_info->handlers->start_transaction( | 614 | smi_info->handlers->start_transaction( |
594 | smi_info->si_sm, msg, 3); | 615 | smi_info->si_sm, msg, 3); |
595 | smi_info->si_state = SI_ENABLE_INTERRUPTS2; | 616 | smi_info->si_state = SI_ENABLE_INTERRUPTS2; |
@@ -611,6 +632,45 @@ static void handle_transaction_done(struct smi_info *smi_info) | |||
611 | smi_info->si_state = SI_NORMAL; | 632 | smi_info->si_state = SI_NORMAL; |
612 | break; | 633 | break; |
613 | } | 634 | } |
635 | |||
636 | case SI_DISABLE_INTERRUPTS1: | ||
637 | { | ||
638 | unsigned char msg[4]; | ||
639 | |||
640 | /* We got the flags from the SMI, now handle them. */ | ||
641 | smi_info->handlers->get_result(smi_info->si_sm, msg, 4); | ||
642 | if (msg[2] != 0) { | ||
643 | printk(KERN_WARNING | ||
644 | "ipmi_si: Could not disable interrupts" | ||
645 | ", failed get.\n"); | ||
646 | smi_info->si_state = SI_NORMAL; | ||
647 | } else { | ||
648 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | ||
649 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; | ||
650 | msg[2] = (msg[3] & | ||
651 | ~(IPMI_BMC_RCV_MSG_INTR | | ||
652 | IPMI_BMC_EVT_MSG_INTR)); | ||
653 | smi_info->handlers->start_transaction( | ||
654 | smi_info->si_sm, msg, 3); | ||
655 | smi_info->si_state = SI_DISABLE_INTERRUPTS2; | ||
656 | } | ||
657 | break; | ||
658 | } | ||
659 | |||
660 | case SI_DISABLE_INTERRUPTS2: | ||
661 | { | ||
662 | unsigned char msg[4]; | ||
663 | |||
664 | /* We got the flags from the SMI, now handle them. */ | ||
665 | smi_info->handlers->get_result(smi_info->si_sm, msg, 4); | ||
666 | if (msg[2] != 0) { | ||
667 | printk(KERN_WARNING | ||
668 | "ipmi_si: Could not disable interrupts" | ||
669 | ", failed set.\n"); | ||
670 | } | ||
671 | smi_info->si_state = SI_NORMAL; | ||
672 | break; | ||
673 | } | ||
614 | } | 674 | } |
615 | } | 675 | } |
616 | 676 | ||
@@ -864,9 +924,6 @@ static void smi_timeout(unsigned long data) | |||
864 | struct timeval t; | 924 | struct timeval t; |
865 | #endif | 925 | #endif |
866 | 926 | ||
867 | if (atomic_read(&smi_info->stop_operation)) | ||
868 | return; | ||
869 | |||
870 | spin_lock_irqsave(&(smi_info->si_lock), flags); | 927 | spin_lock_irqsave(&(smi_info->si_lock), flags); |
871 | #ifdef DEBUG_TIMING | 928 | #ifdef DEBUG_TIMING |
872 | do_gettimeofday(&t); | 929 | do_gettimeofday(&t); |
@@ -922,15 +979,11 @@ static irqreturn_t si_irq_handler(int irq, void *data) | |||
922 | smi_info->interrupts++; | 979 | smi_info->interrupts++; |
923 | spin_unlock(&smi_info->count_lock); | 980 | spin_unlock(&smi_info->count_lock); |
924 | 981 | ||
925 | if (atomic_read(&smi_info->stop_operation)) | ||
926 | goto out; | ||
927 | |||
928 | #ifdef DEBUG_TIMING | 982 | #ifdef DEBUG_TIMING |
929 | do_gettimeofday(&t); | 983 | do_gettimeofday(&t); |
930 | printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 984 | printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
931 | #endif | 985 | #endif |
932 | smi_event_handler(smi_info, 0); | 986 | smi_event_handler(smi_info, 0); |
933 | out: | ||
934 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); | 987 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); |
935 | return IRQ_HANDLED; | 988 | return IRQ_HANDLED; |
936 | } | 989 | } |
@@ -1118,7 +1171,7 @@ static int std_irq_setup(struct smi_info *info) | |||
1118 | if (info->si_type == SI_BT) { | 1171 | if (info->si_type == SI_BT) { |
1119 | rv = request_irq(info->irq, | 1172 | rv = request_irq(info->irq, |
1120 | si_bt_irq_handler, | 1173 | si_bt_irq_handler, |
1121 | IRQF_DISABLED, | 1174 | IRQF_SHARED | IRQF_DISABLED, |
1122 | DEVICE_NAME, | 1175 | DEVICE_NAME, |
1123 | info); | 1176 | info); |
1124 | if (!rv) | 1177 | if (!rv) |
@@ -1128,7 +1181,7 @@ static int std_irq_setup(struct smi_info *info) | |||
1128 | } else | 1181 | } else |
1129 | rv = request_irq(info->irq, | 1182 | rv = request_irq(info->irq, |
1130 | si_irq_handler, | 1183 | si_irq_handler, |
1131 | IRQF_DISABLED, | 1184 | IRQF_SHARED | IRQF_DISABLED, |
1132 | DEVICE_NAME, | 1185 | DEVICE_NAME, |
1133 | info); | 1186 | info); |
1134 | if (rv) { | 1187 | if (rv) { |
@@ -1708,15 +1761,11 @@ static u32 ipmi_acpi_gpe(void *context) | |||
1708 | smi_info->interrupts++; | 1761 | smi_info->interrupts++; |
1709 | spin_unlock(&smi_info->count_lock); | 1762 | spin_unlock(&smi_info->count_lock); |
1710 | 1763 | ||
1711 | if (atomic_read(&smi_info->stop_operation)) | ||
1712 | goto out; | ||
1713 | |||
1714 | #ifdef DEBUG_TIMING | 1764 | #ifdef DEBUG_TIMING |
1715 | do_gettimeofday(&t); | 1765 | do_gettimeofday(&t); |
1716 | printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 1766 | printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
1717 | #endif | 1767 | #endif |
1718 | smi_event_handler(smi_info, 0); | 1768 | smi_event_handler(smi_info, 0); |
1719 | out: | ||
1720 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); | 1769 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); |
1721 | 1770 | ||
1722 | return ACPI_INTERRUPT_HANDLED; | 1771 | return ACPI_INTERRUPT_HANDLED; |
@@ -2942,28 +2991,33 @@ static void cleanup_one_si(struct smi_info *to_clean) | |||
2942 | 2991 | ||
2943 | list_del(&to_clean->link); | 2992 | list_del(&to_clean->link); |
2944 | 2993 | ||
2945 | /* Tell the timer and interrupt handlers that we are shutting | 2994 | /* Tell the driver that we are shutting down. */ |
2946 | down. */ | ||
2947 | spin_lock_irqsave(&(to_clean->si_lock), flags); | ||
2948 | spin_lock(&(to_clean->msg_lock)); | ||
2949 | |||
2950 | atomic_inc(&to_clean->stop_operation); | 2995 | atomic_inc(&to_clean->stop_operation); |
2951 | 2996 | ||
2952 | if (to_clean->irq_cleanup) | 2997 | /* Make sure the timer and thread are stopped and will not run |
2953 | to_clean->irq_cleanup(to_clean); | 2998 | again. */ |
2954 | |||
2955 | spin_unlock(&(to_clean->msg_lock)); | ||
2956 | spin_unlock_irqrestore(&(to_clean->si_lock), flags); | ||
2957 | |||
2958 | /* Wait until we know that we are out of any interrupt | ||
2959 | handlers might have been running before we freed the | ||
2960 | interrupt. */ | ||
2961 | synchronize_sched(); | ||
2962 | |||
2963 | wait_for_timer_and_thread(to_clean); | 2999 | wait_for_timer_and_thread(to_clean); |
2964 | 3000 | ||
2965 | /* Interrupts and timeouts are stopped, now make sure the | 3001 | /* Timeouts are stopped, now make sure the interrupts are off |
2966 | interface is in a clean state. */ | 3002 | for the device. A little tricky with locks to make sure |
3003 | there are no races. */ | ||
3004 | spin_lock_irqsave(&to_clean->si_lock, flags); | ||
3005 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | ||
3006 | spin_unlock_irqrestore(&to_clean->si_lock, flags); | ||
3007 | poll(to_clean); | ||
3008 | schedule_timeout_uninterruptible(1); | ||
3009 | spin_lock_irqsave(&to_clean->si_lock, flags); | ||
3010 | } | ||
3011 | disable_si_irq(to_clean); | ||
3012 | spin_unlock_irqrestore(&to_clean->si_lock, flags); | ||
3013 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | ||
3014 | poll(to_clean); | ||
3015 | schedule_timeout_uninterruptible(1); | ||
3016 | } | ||
3017 | |||
3018 | /* Clean up interrupts and make sure that everything is done. */ | ||
3019 | if (to_clean->irq_cleanup) | ||
3020 | to_clean->irq_cleanup(to_clean); | ||
2967 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | 3021 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { |
2968 | poll(to_clean); | 3022 | poll(to_clean); |
2969 | schedule_timeout_uninterruptible(1); | 3023 | schedule_timeout_uninterruptible(1); |