diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_watchdog.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 259 |
1 files changed, 205 insertions, 54 deletions
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 2da64bf7469c..1f3159eb1ede 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c | |||
@@ -47,6 +47,9 @@ | |||
47 | #include <linux/reboot.h> | 47 | #include <linux/reboot.h> |
48 | #include <linux/wait.h> | 48 | #include <linux/wait.h> |
49 | #include <linux/poll.h> | 49 | #include <linux/poll.h> |
50 | #include <linux/string.h> | ||
51 | #include <linux/ctype.h> | ||
52 | #include <asm/atomic.h> | ||
50 | #ifdef CONFIG_X86_LOCAL_APIC | 53 | #ifdef CONFIG_X86_LOCAL_APIC |
51 | #include <asm/apic.h> | 54 | #include <asm/apic.h> |
52 | #endif | 55 | #endif |
@@ -158,27 +161,120 @@ static struct fasync_struct *fasync_q = NULL; | |||
158 | static char pretimeout_since_last_heartbeat = 0; | 161 | static char pretimeout_since_last_heartbeat = 0; |
159 | static char expect_close; | 162 | static char expect_close; |
160 | 163 | ||
164 | static DECLARE_RWSEM(register_sem); | ||
165 | |||
166 | /* Parameters to ipmi_set_timeout */ | ||
167 | #define IPMI_SET_TIMEOUT_NO_HB 0 | ||
168 | #define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 | ||
169 | #define IPMI_SET_TIMEOUT_FORCE_HB 2 | ||
170 | |||
171 | static int ipmi_set_timeout(int do_heartbeat); | ||
172 | |||
161 | /* If true, the driver will start running as soon as it is configured | 173 | /* If true, the driver will start running as soon as it is configured |
162 | and ready. */ | 174 | and ready. */ |
163 | static int start_now = 0; | 175 | static int start_now = 0; |
164 | 176 | ||
165 | module_param(timeout, int, 0); | 177 | static int set_param_int(const char *val, struct kernel_param *kp) |
178 | { | ||
179 | char *endp; | ||
180 | int l; | ||
181 | int rv = 0; | ||
182 | |||
183 | if (!val) | ||
184 | return -EINVAL; | ||
185 | l = simple_strtoul(val, &endp, 0); | ||
186 | if (endp == val) | ||
187 | return -EINVAL; | ||
188 | |||
189 | down_read(®ister_sem); | ||
190 | *((int *)kp->arg) = l; | ||
191 | if (watchdog_user) | ||
192 | rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); | ||
193 | up_read(®ister_sem); | ||
194 | |||
195 | return rv; | ||
196 | } | ||
197 | |||
198 | static int get_param_int(char *buffer, struct kernel_param *kp) | ||
199 | { | ||
200 | return sprintf(buffer, "%i", *((int *)kp->arg)); | ||
201 | } | ||
202 | |||
203 | typedef int (*action_fn)(const char *intval, char *outval); | ||
204 | |||
205 | static int action_op(const char *inval, char *outval); | ||
206 | static int preaction_op(const char *inval, char *outval); | ||
207 | static int preop_op(const char *inval, char *outval); | ||
208 | static void check_parms(void); | ||
209 | |||
210 | static int set_param_str(const char *val, struct kernel_param *kp) | ||
211 | { | ||
212 | action_fn fn = (action_fn) kp->arg; | ||
213 | int rv = 0; | ||
214 | const char *end; | ||
215 | char valcp[16]; | ||
216 | int len; | ||
217 | |||
218 | /* Truncate leading and trailing spaces. */ | ||
219 | while (isspace(*val)) | ||
220 | val++; | ||
221 | end = val + strlen(val) - 1; | ||
222 | while ((end >= val) && isspace(*end)) | ||
223 | end--; | ||
224 | len = end - val + 1; | ||
225 | if (len > sizeof(valcp) - 1) | ||
226 | return -EINVAL; | ||
227 | memcpy(valcp, val, len); | ||
228 | valcp[len] = '\0'; | ||
229 | |||
230 | down_read(®ister_sem); | ||
231 | rv = fn(valcp, NULL); | ||
232 | if (rv) | ||
233 | goto out_unlock; | ||
234 | |||
235 | check_parms(); | ||
236 | if (watchdog_user) | ||
237 | rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); | ||
238 | |||
239 | out_unlock: | ||
240 | up_read(®ister_sem); | ||
241 | return rv; | ||
242 | } | ||
243 | |||
244 | static int get_param_str(char *buffer, struct kernel_param *kp) | ||
245 | { | ||
246 | action_fn fn = (action_fn) kp->arg; | ||
247 | int rv; | ||
248 | |||
249 | rv = fn(NULL, buffer); | ||
250 | if (rv) | ||
251 | return rv; | ||
252 | return strlen(buffer); | ||
253 | } | ||
254 | |||
255 | module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644); | ||
166 | MODULE_PARM_DESC(timeout, "Timeout value in seconds."); | 256 | MODULE_PARM_DESC(timeout, "Timeout value in seconds."); |
167 | module_param(pretimeout, int, 0); | 257 | |
258 | module_param_call(pretimeout, set_param_int, get_param_int, &pretimeout, 0644); | ||
168 | MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); | 259 | MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); |
169 | module_param_string(action, action, sizeof(action), 0); | 260 | |
261 | module_param_call(action, set_param_str, get_param_str, action_op, 0644); | ||
170 | MODULE_PARM_DESC(action, "Timeout action. One of: " | 262 | MODULE_PARM_DESC(action, "Timeout action. One of: " |
171 | "reset, none, power_cycle, power_off."); | 263 | "reset, none, power_cycle, power_off."); |
172 | module_param_string(preaction, preaction, sizeof(preaction), 0); | 264 | |
265 | module_param_call(preaction, set_param_str, get_param_str, preaction_op, 0644); | ||
173 | MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " | 266 | MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " |
174 | "pre_none, pre_smi, pre_nmi, pre_int."); | 267 | "pre_none, pre_smi, pre_nmi, pre_int."); |
175 | module_param_string(preop, preop, sizeof(preop), 0); | 268 | |
269 | module_param_call(preop, set_param_str, get_param_str, preop_op, 0644); | ||
176 | MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " | 270 | MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " |
177 | "preop_none, preop_panic, preop_give_data."); | 271 | "preop_none, preop_panic, preop_give_data."); |
272 | |||
178 | module_param(start_now, int, 0); | 273 | module_param(start_now, int, 0); |
179 | MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" | 274 | MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" |
180 | "soon as the driver is loaded."); | 275 | "soon as the driver is loaded."); |
181 | module_param(nowayout, int, 0); | 276 | |
277 | module_param(nowayout, int, 0644); | ||
182 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | 278 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); |
183 | 279 | ||
184 | /* Default state of the timer. */ | 280 | /* Default state of the timer. */ |
@@ -200,6 +296,8 @@ static int ipmi_start_timer_on_heartbeat = 0; | |||
200 | static unsigned char ipmi_version_major; | 296 | static unsigned char ipmi_version_major; |
201 | static unsigned char ipmi_version_minor; | 297 | static unsigned char ipmi_version_minor; |
202 | 298 | ||
299 | /* If a pretimeout occurs, this is used to allow only one panic to happen. */ | ||
300 | static atomic_t preop_panic_excl = ATOMIC_INIT(-1); | ||
203 | 301 | ||
204 | static int ipmi_heartbeat(void); | 302 | static int ipmi_heartbeat(void); |
205 | static void panic_halt_ipmi_heartbeat(void); | 303 | static void panic_halt_ipmi_heartbeat(void); |
@@ -294,11 +392,6 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, | |||
294 | return rv; | 392 | return rv; |
295 | } | 393 | } |
296 | 394 | ||
297 | /* Parameters to ipmi_set_timeout */ | ||
298 | #define IPMI_SET_TIMEOUT_NO_HB 0 | ||
299 | #define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 | ||
300 | #define IPMI_SET_TIMEOUT_FORCE_HB 2 | ||
301 | |||
302 | static int ipmi_set_timeout(int do_heartbeat) | 395 | static int ipmi_set_timeout(int do_heartbeat) |
303 | { | 396 | { |
304 | int send_heartbeat_now; | 397 | int send_heartbeat_now; |
@@ -732,8 +825,6 @@ static struct miscdevice ipmi_wdog_miscdev = { | |||
732 | .fops = &ipmi_wdog_fops | 825 | .fops = &ipmi_wdog_fops |
733 | }; | 826 | }; |
734 | 827 | ||
735 | static DECLARE_RWSEM(register_sem); | ||
736 | |||
737 | static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, | 828 | static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, |
738 | void *handler_data) | 829 | void *handler_data) |
739 | { | 830 | { |
@@ -749,9 +840,10 @@ static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, | |||
749 | static void ipmi_wdog_pretimeout_handler(void *handler_data) | 840 | static void ipmi_wdog_pretimeout_handler(void *handler_data) |
750 | { | 841 | { |
751 | if (preaction_val != WDOG_PRETIMEOUT_NONE) { | 842 | if (preaction_val != WDOG_PRETIMEOUT_NONE) { |
752 | if (preop_val == WDOG_PREOP_PANIC) | 843 | if (preop_val == WDOG_PREOP_PANIC) { |
753 | panic("Watchdog pre-timeout"); | 844 | if (atomic_inc_and_test(&preop_panic_excl)) |
754 | else if (preop_val == WDOG_PREOP_GIVE_DATA) { | 845 | panic("Watchdog pre-timeout"); |
846 | } else if (preop_val == WDOG_PREOP_GIVE_DATA) { | ||
755 | spin_lock(&ipmi_read_lock); | 847 | spin_lock(&ipmi_read_lock); |
756 | data_to_read = 1; | 848 | data_to_read = 1; |
757 | wake_up_interruptible(&read_q); | 849 | wake_up_interruptible(&read_q); |
@@ -825,7 +917,8 @@ ipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled) | |||
825 | an error and not work unless we re-enable | 917 | an error and not work unless we re-enable |
826 | the timer. So do so. */ | 918 | the timer. So do so. */ |
827 | pretimeout_since_last_heartbeat = 1; | 919 | pretimeout_since_last_heartbeat = 1; |
828 | panic(PFX "pre-timeout"); | 920 | if (atomic_inc_and_test(&preop_panic_excl)) |
921 | panic(PFX "pre-timeout"); | ||
829 | } | 922 | } |
830 | 923 | ||
831 | return NOTIFY_DONE; | 924 | return NOTIFY_DONE; |
@@ -839,6 +932,7 @@ static struct nmi_handler ipmi_nmi_handler = | |||
839 | .handler = ipmi_nmi, | 932 | .handler = ipmi_nmi, |
840 | .priority = 0, /* Call us last. */ | 933 | .priority = 0, /* Call us last. */ |
841 | }; | 934 | }; |
935 | int nmi_handler_registered; | ||
842 | #endif | 936 | #endif |
843 | 937 | ||
844 | static int wdog_reboot_handler(struct notifier_block *this, | 938 | static int wdog_reboot_handler(struct notifier_block *this, |
@@ -921,59 +1015,86 @@ static struct ipmi_smi_watcher smi_watcher = | |||
921 | .smi_gone = ipmi_smi_gone | 1015 | .smi_gone = ipmi_smi_gone |
922 | }; | 1016 | }; |
923 | 1017 | ||
924 | static int __init ipmi_wdog_init(void) | 1018 | static int action_op(const char *inval, char *outval) |
925 | { | 1019 | { |
926 | int rv; | 1020 | if (outval) |
1021 | strcpy(outval, action); | ||
1022 | |||
1023 | if (!inval) | ||
1024 | return 0; | ||
927 | 1025 | ||
928 | if (strcmp(action, "reset") == 0) { | 1026 | if (strcmp(inval, "reset") == 0) |
929 | action_val = WDOG_TIMEOUT_RESET; | 1027 | action_val = WDOG_TIMEOUT_RESET; |
930 | } else if (strcmp(action, "none") == 0) { | 1028 | else if (strcmp(inval, "none") == 0) |
931 | action_val = WDOG_TIMEOUT_NONE; | 1029 | action_val = WDOG_TIMEOUT_NONE; |
932 | } else if (strcmp(action, "power_cycle") == 0) { | 1030 | else if (strcmp(inval, "power_cycle") == 0) |
933 | action_val = WDOG_TIMEOUT_POWER_CYCLE; | 1031 | action_val = WDOG_TIMEOUT_POWER_CYCLE; |
934 | } else if (strcmp(action, "power_off") == 0) { | 1032 | else if (strcmp(inval, "power_off") == 0) |
935 | action_val = WDOG_TIMEOUT_POWER_DOWN; | 1033 | action_val = WDOG_TIMEOUT_POWER_DOWN; |
936 | } else { | 1034 | else |
937 | action_val = WDOG_TIMEOUT_RESET; | 1035 | return -EINVAL; |
938 | printk(KERN_INFO PFX "Unknown action '%s', defaulting to" | 1036 | strcpy(action, inval); |
939 | " reset\n", action); | 1037 | return 0; |
940 | } | 1038 | } |
1039 | |||
1040 | static int preaction_op(const char *inval, char *outval) | ||
1041 | { | ||
1042 | if (outval) | ||
1043 | strcpy(outval, preaction); | ||
941 | 1044 | ||
942 | if (strcmp(preaction, "pre_none") == 0) { | 1045 | if (!inval) |
1046 | return 0; | ||
1047 | |||
1048 | if (strcmp(inval, "pre_none") == 0) | ||
943 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1049 | preaction_val = WDOG_PRETIMEOUT_NONE; |
944 | } else if (strcmp(preaction, "pre_smi") == 0) { | 1050 | else if (strcmp(inval, "pre_smi") == 0) |
945 | preaction_val = WDOG_PRETIMEOUT_SMI; | 1051 | preaction_val = WDOG_PRETIMEOUT_SMI; |
946 | #ifdef HAVE_NMI_HANDLER | 1052 | #ifdef HAVE_NMI_HANDLER |
947 | } else if (strcmp(preaction, "pre_nmi") == 0) { | 1053 | else if (strcmp(inval, "pre_nmi") == 0) |
948 | preaction_val = WDOG_PRETIMEOUT_NMI; | 1054 | preaction_val = WDOG_PRETIMEOUT_NMI; |
949 | #endif | 1055 | #endif |
950 | } else if (strcmp(preaction, "pre_int") == 0) { | 1056 | else if (strcmp(inval, "pre_int") == 0) |
951 | preaction_val = WDOG_PRETIMEOUT_MSG_INT; | 1057 | preaction_val = WDOG_PRETIMEOUT_MSG_INT; |
952 | } else { | 1058 | else |
953 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1059 | return -EINVAL; |
954 | printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" | 1060 | strcpy(preaction, inval); |
955 | " none\n", preaction); | 1061 | return 0; |
956 | } | 1062 | } |
1063 | |||
1064 | static int preop_op(const char *inval, char *outval) | ||
1065 | { | ||
1066 | if (outval) | ||
1067 | strcpy(outval, preop); | ||
957 | 1068 | ||
958 | if (strcmp(preop, "preop_none") == 0) { | 1069 | if (!inval) |
1070 | return 0; | ||
1071 | |||
1072 | if (strcmp(inval, "preop_none") == 0) | ||
959 | preop_val = WDOG_PREOP_NONE; | 1073 | preop_val = WDOG_PREOP_NONE; |
960 | } else if (strcmp(preop, "preop_panic") == 0) { | 1074 | else if (strcmp(inval, "preop_panic") == 0) |
961 | preop_val = WDOG_PREOP_PANIC; | 1075 | preop_val = WDOG_PREOP_PANIC; |
962 | } else if (strcmp(preop, "preop_give_data") == 0) { | 1076 | else if (strcmp(inval, "preop_give_data") == 0) |
963 | preop_val = WDOG_PREOP_GIVE_DATA; | 1077 | preop_val = WDOG_PREOP_GIVE_DATA; |
964 | } else { | 1078 | else |
965 | preop_val = WDOG_PREOP_NONE; | 1079 | return -EINVAL; |
966 | printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" | 1080 | strcpy(preop, inval); |
967 | " none\n", preop); | 1081 | return 0; |
968 | } | 1082 | } |
969 | 1083 | ||
1084 | static void check_parms(void) | ||
1085 | { | ||
970 | #ifdef HAVE_NMI_HANDLER | 1086 | #ifdef HAVE_NMI_HANDLER |
1087 | int do_nmi = 0; | ||
1088 | int rv; | ||
1089 | |||
971 | if (preaction_val == WDOG_PRETIMEOUT_NMI) { | 1090 | if (preaction_val == WDOG_PRETIMEOUT_NMI) { |
1091 | do_nmi = 1; | ||
972 | if (preop_val == WDOG_PREOP_GIVE_DATA) { | 1092 | if (preop_val == WDOG_PREOP_GIVE_DATA) { |
973 | printk(KERN_WARNING PFX "Pretimeout op is to give data" | 1093 | printk(KERN_WARNING PFX "Pretimeout op is to give data" |
974 | " but NMI pretimeout is enabled, setting" | 1094 | " but NMI pretimeout is enabled, setting" |
975 | " pretimeout op to none\n"); | 1095 | " pretimeout op to none\n"); |
976 | preop_val = WDOG_PREOP_NONE; | 1096 | preop_op("preop_none", NULL); |
1097 | do_nmi = 0; | ||
977 | } | 1098 | } |
978 | #ifdef CONFIG_X86_LOCAL_APIC | 1099 | #ifdef CONFIG_X86_LOCAL_APIC |
979 | if (nmi_watchdog == NMI_IO_APIC) { | 1100 | if (nmi_watchdog == NMI_IO_APIC) { |
@@ -983,18 +1104,48 @@ static int __init ipmi_wdog_init(void) | |||
983 | " Disabling IPMI nmi pretimeout.\n", | 1104 | " Disabling IPMI nmi pretimeout.\n", |
984 | nmi_watchdog); | 1105 | nmi_watchdog); |
985 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1106 | preaction_val = WDOG_PRETIMEOUT_NONE; |
986 | } else { | 1107 | do_nmi = 0; |
1108 | } | ||
987 | #endif | 1109 | #endif |
1110 | } | ||
1111 | if (do_nmi && !nmi_handler_registered) { | ||
988 | rv = request_nmi(&ipmi_nmi_handler); | 1112 | rv = request_nmi(&ipmi_nmi_handler); |
989 | if (rv) { | 1113 | if (rv) { |
990 | printk(KERN_WARNING PFX "Can't register nmi handler\n"); | 1114 | printk(KERN_WARNING PFX |
991 | return rv; | 1115 | "Can't register nmi handler\n"); |
992 | } | 1116 | return; |
993 | #ifdef CONFIG_X86_LOCAL_APIC | 1117 | } else |
994 | } | 1118 | nmi_handler_registered = 1; |
995 | #endif | 1119 | } else if (!do_nmi && nmi_handler_registered) { |
1120 | release_nmi(&ipmi_nmi_handler); | ||
1121 | nmi_handler_registered = 0; | ||
996 | } | 1122 | } |
997 | #endif | 1123 | #endif |
1124 | } | ||
1125 | |||
1126 | static int __init ipmi_wdog_init(void) | ||
1127 | { | ||
1128 | int rv; | ||
1129 | |||
1130 | if (action_op(action, NULL)) { | ||
1131 | action_op("reset", NULL); | ||
1132 | printk(KERN_INFO PFX "Unknown action '%s', defaulting to" | ||
1133 | " reset\n", action); | ||
1134 | } | ||
1135 | |||
1136 | if (preaction_op(preaction, NULL)) { | ||
1137 | preaction_op("pre_none", NULL); | ||
1138 | printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" | ||
1139 | " none\n", preaction); | ||
1140 | } | ||
1141 | |||
1142 | if (preop_op(preop, NULL)) { | ||
1143 | preop_op("preop_none", NULL); | ||
1144 | printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" | ||
1145 | " none\n", preop); | ||
1146 | } | ||
1147 | |||
1148 | check_parms(); | ||
998 | 1149 | ||
999 | rv = ipmi_smi_watcher_register(&smi_watcher); | 1150 | rv = ipmi_smi_watcher_register(&smi_watcher); |
1000 | if (rv) { | 1151 | if (rv) { |
@@ -1021,7 +1172,7 @@ static __exit void ipmi_unregister_watchdog(void) | |||
1021 | down_write(®ister_sem); | 1172 | down_write(®ister_sem); |
1022 | 1173 | ||
1023 | #ifdef HAVE_NMI_HANDLER | 1174 | #ifdef HAVE_NMI_HANDLER |
1024 | if (preaction_val == WDOG_PRETIMEOUT_NMI) | 1175 | if (nmi_handler_registered) |
1025 | release_nmi(&ipmi_nmi_handler); | 1176 | release_nmi(&ipmi_nmi_handler); |
1026 | #endif | 1177 | #endif |
1027 | 1178 | ||