diff options
author | Corey Minyard <minyard@acm.org> | 2005-11-07 03:59:57 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-07 10:53:44 -0500 |
commit | cc4673eecdcc4a918e4d8796295d798b5e98d602 (patch) | |
tree | 7956c1c9a310df75f459d650d19ebe5fbf29f583 /drivers/char/ipmi/ipmi_watchdog.c | |
parent | c4edff1c19ef23e15aae64ca03f32c6719822d54 (diff) |
[PATCH] ipmi: watchdog parms in sysfs
Modify the IPMI watchdog parameters (the ones that make sense) to be exported
from sysfs. This is somewhat complicated because these parameters have
side-effects that must be handled.
Signed-off-by: Corey Minyard <minyard@acm.org>
Cc: Matt Domsch <Matt_Domsch@dell.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/ipmi/ipmi_watchdog.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 246 |
1 files changed, 196 insertions, 50 deletions
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 2da64bf7469c..405697a9f4d9 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c | |||
@@ -47,6 +47,8 @@ | |||
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> | ||
50 | #ifdef CONFIG_X86_LOCAL_APIC | 52 | #ifdef CONFIG_X86_LOCAL_APIC |
51 | #include <asm/apic.h> | 53 | #include <asm/apic.h> |
52 | #endif | 54 | #endif |
@@ -158,27 +160,120 @@ static struct fasync_struct *fasync_q = NULL; | |||
158 | static char pretimeout_since_last_heartbeat = 0; | 160 | static char pretimeout_since_last_heartbeat = 0; |
159 | static char expect_close; | 161 | static char expect_close; |
160 | 162 | ||
163 | static DECLARE_RWSEM(register_sem); | ||
164 | |||
165 | /* Parameters to ipmi_set_timeout */ | ||
166 | #define IPMI_SET_TIMEOUT_NO_HB 0 | ||
167 | #define IPMI_SET_TIMEOUT_HB_IF_NECESSARY 1 | ||
168 | #define IPMI_SET_TIMEOUT_FORCE_HB 2 | ||
169 | |||
170 | static int ipmi_set_timeout(int do_heartbeat); | ||
171 | |||
161 | /* If true, the driver will start running as soon as it is configured | 172 | /* If true, the driver will start running as soon as it is configured |
162 | and ready. */ | 173 | and ready. */ |
163 | static int start_now = 0; | 174 | static int start_now = 0; |
164 | 175 | ||
165 | module_param(timeout, int, 0); | 176 | static int set_param_int(const char *val, struct kernel_param *kp) |
177 | { | ||
178 | char *endp; | ||
179 | int l; | ||
180 | int rv = 0; | ||
181 | |||
182 | if (!val) | ||
183 | return -EINVAL; | ||
184 | l = simple_strtoul(val, &endp, 0); | ||
185 | if (endp == val) | ||
186 | return -EINVAL; | ||
187 | |||
188 | down_read(®ister_sem); | ||
189 | *((int *)kp->arg) = l; | ||
190 | if (watchdog_user) | ||
191 | rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); | ||
192 | up_read(®ister_sem); | ||
193 | |||
194 | return rv; | ||
195 | } | ||
196 | |||
197 | static int get_param_int(char *buffer, struct kernel_param *kp) | ||
198 | { | ||
199 | return sprintf(buffer, "%i", *((int *)kp->arg)); | ||
200 | } | ||
201 | |||
202 | typedef int (*action_fn)(const char *intval, char *outval); | ||
203 | |||
204 | static int action_op(const char *inval, char *outval); | ||
205 | static int preaction_op(const char *inval, char *outval); | ||
206 | static int preop_op(const char *inval, char *outval); | ||
207 | static void check_parms(void); | ||
208 | |||
209 | static int set_param_str(const char *val, struct kernel_param *kp) | ||
210 | { | ||
211 | action_fn fn = (action_fn) kp->arg; | ||
212 | int rv = 0; | ||
213 | const char *end; | ||
214 | char valcp[16]; | ||
215 | int len; | ||
216 | |||
217 | /* Truncate leading and trailing spaces. */ | ||
218 | while (isspace(*val)) | ||
219 | val++; | ||
220 | end = val + strlen(val) - 1; | ||
221 | while ((end >= val) && isspace(*end)) | ||
222 | end--; | ||
223 | len = end - val + 1; | ||
224 | if (len > sizeof(valcp) - 1) | ||
225 | return -EINVAL; | ||
226 | memcpy(valcp, val, len); | ||
227 | valcp[len] = '\0'; | ||
228 | |||
229 | down_read(®ister_sem); | ||
230 | rv = fn(valcp, NULL); | ||
231 | if (rv) | ||
232 | goto out_unlock; | ||
233 | |||
234 | check_parms(); | ||
235 | if (watchdog_user) | ||
236 | rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); | ||
237 | |||
238 | out_unlock: | ||
239 | up_read(®ister_sem); | ||
240 | return rv; | ||
241 | } | ||
242 | |||
243 | static int get_param_str(char *buffer, struct kernel_param *kp) | ||
244 | { | ||
245 | action_fn fn = (action_fn) kp->arg; | ||
246 | int rv; | ||
247 | |||
248 | rv = fn(NULL, buffer); | ||
249 | if (rv) | ||
250 | return rv; | ||
251 | return strlen(buffer); | ||
252 | } | ||
253 | |||
254 | module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644); | ||
166 | MODULE_PARM_DESC(timeout, "Timeout value in seconds."); | 255 | MODULE_PARM_DESC(timeout, "Timeout value in seconds."); |
167 | module_param(pretimeout, int, 0); | 256 | |
257 | module_param_call(pretimeout, set_param_int, get_param_int, &pretimeout, 0644); | ||
168 | MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); | 258 | MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); |
169 | module_param_string(action, action, sizeof(action), 0); | 259 | |
260 | module_param_call(action, set_param_str, get_param_str, action_op, 0644); | ||
170 | MODULE_PARM_DESC(action, "Timeout action. One of: " | 261 | MODULE_PARM_DESC(action, "Timeout action. One of: " |
171 | "reset, none, power_cycle, power_off."); | 262 | "reset, none, power_cycle, power_off."); |
172 | module_param_string(preaction, preaction, sizeof(preaction), 0); | 263 | |
264 | module_param_call(preaction, set_param_str, get_param_str, preaction_op, 0644); | ||
173 | MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " | 265 | MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " |
174 | "pre_none, pre_smi, pre_nmi, pre_int."); | 266 | "pre_none, pre_smi, pre_nmi, pre_int."); |
175 | module_param_string(preop, preop, sizeof(preop), 0); | 267 | |
268 | module_param_call(preop, set_param_str, get_param_str, preop_op, 0644); | ||
176 | MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " | 269 | MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " |
177 | "preop_none, preop_panic, preop_give_data."); | 270 | "preop_none, preop_panic, preop_give_data."); |
271 | |||
178 | module_param(start_now, int, 0); | 272 | module_param(start_now, int, 0); |
179 | MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" | 273 | MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" |
180 | "soon as the driver is loaded."); | 274 | "soon as the driver is loaded."); |
181 | module_param(nowayout, int, 0); | 275 | |
276 | module_param(nowayout, int, 0644); | ||
182 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); | 277 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); |
183 | 278 | ||
184 | /* Default state of the timer. */ | 279 | /* Default state of the timer. */ |
@@ -294,11 +389,6 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, | |||
294 | return rv; | 389 | return rv; |
295 | } | 390 | } |
296 | 391 | ||
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) | 392 | static int ipmi_set_timeout(int do_heartbeat) |
303 | { | 393 | { |
304 | int send_heartbeat_now; | 394 | int send_heartbeat_now; |
@@ -732,8 +822,6 @@ static struct miscdevice ipmi_wdog_miscdev = { | |||
732 | .fops = &ipmi_wdog_fops | 822 | .fops = &ipmi_wdog_fops |
733 | }; | 823 | }; |
734 | 824 | ||
735 | static DECLARE_RWSEM(register_sem); | ||
736 | |||
737 | static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, | 825 | static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, |
738 | void *handler_data) | 826 | void *handler_data) |
739 | { | 827 | { |
@@ -839,6 +927,7 @@ static struct nmi_handler ipmi_nmi_handler = | |||
839 | .handler = ipmi_nmi, | 927 | .handler = ipmi_nmi, |
840 | .priority = 0, /* Call us last. */ | 928 | .priority = 0, /* Call us last. */ |
841 | }; | 929 | }; |
930 | int nmi_handler_registered; | ||
842 | #endif | 931 | #endif |
843 | 932 | ||
844 | static int wdog_reboot_handler(struct notifier_block *this, | 933 | static int wdog_reboot_handler(struct notifier_block *this, |
@@ -921,59 +1010,86 @@ static struct ipmi_smi_watcher smi_watcher = | |||
921 | .smi_gone = ipmi_smi_gone | 1010 | .smi_gone = ipmi_smi_gone |
922 | }; | 1011 | }; |
923 | 1012 | ||
924 | static int __init ipmi_wdog_init(void) | 1013 | static int action_op(const char *inval, char *outval) |
925 | { | 1014 | { |
926 | int rv; | 1015 | if (outval) |
1016 | strcpy(outval, action); | ||
1017 | |||
1018 | if (!inval) | ||
1019 | return 0; | ||
927 | 1020 | ||
928 | if (strcmp(action, "reset") == 0) { | 1021 | if (strcmp(inval, "reset") == 0) |
929 | action_val = WDOG_TIMEOUT_RESET; | 1022 | action_val = WDOG_TIMEOUT_RESET; |
930 | } else if (strcmp(action, "none") == 0) { | 1023 | else if (strcmp(inval, "none") == 0) |
931 | action_val = WDOG_TIMEOUT_NONE; | 1024 | action_val = WDOG_TIMEOUT_NONE; |
932 | } else if (strcmp(action, "power_cycle") == 0) { | 1025 | else if (strcmp(inval, "power_cycle") == 0) |
933 | action_val = WDOG_TIMEOUT_POWER_CYCLE; | 1026 | action_val = WDOG_TIMEOUT_POWER_CYCLE; |
934 | } else if (strcmp(action, "power_off") == 0) { | 1027 | else if (strcmp(inval, "power_off") == 0) |
935 | action_val = WDOG_TIMEOUT_POWER_DOWN; | 1028 | action_val = WDOG_TIMEOUT_POWER_DOWN; |
936 | } else { | 1029 | else |
937 | action_val = WDOG_TIMEOUT_RESET; | 1030 | return -EINVAL; |
938 | printk(KERN_INFO PFX "Unknown action '%s', defaulting to" | 1031 | strcpy(action, inval); |
939 | " reset\n", action); | 1032 | return 0; |
940 | } | 1033 | } |
1034 | |||
1035 | static int preaction_op(const char *inval, char *outval) | ||
1036 | { | ||
1037 | if (outval) | ||
1038 | strcpy(outval, preaction); | ||
941 | 1039 | ||
942 | if (strcmp(preaction, "pre_none") == 0) { | 1040 | if (!inval) |
1041 | return 0; | ||
1042 | |||
1043 | if (strcmp(inval, "pre_none") == 0) | ||
943 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1044 | preaction_val = WDOG_PRETIMEOUT_NONE; |
944 | } else if (strcmp(preaction, "pre_smi") == 0) { | 1045 | else if (strcmp(inval, "pre_smi") == 0) |
945 | preaction_val = WDOG_PRETIMEOUT_SMI; | 1046 | preaction_val = WDOG_PRETIMEOUT_SMI; |
946 | #ifdef HAVE_NMI_HANDLER | 1047 | #ifdef HAVE_NMI_HANDLER |
947 | } else if (strcmp(preaction, "pre_nmi") == 0) { | 1048 | else if (strcmp(inval, "pre_nmi") == 0) |
948 | preaction_val = WDOG_PRETIMEOUT_NMI; | 1049 | preaction_val = WDOG_PRETIMEOUT_NMI; |
949 | #endif | 1050 | #endif |
950 | } else if (strcmp(preaction, "pre_int") == 0) { | 1051 | else if (strcmp(inval, "pre_int") == 0) |
951 | preaction_val = WDOG_PRETIMEOUT_MSG_INT; | 1052 | preaction_val = WDOG_PRETIMEOUT_MSG_INT; |
952 | } else { | 1053 | else |
953 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1054 | return -EINVAL; |
954 | printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" | 1055 | strcpy(preaction, inval); |
955 | " none\n", preaction); | 1056 | return 0; |
956 | } | 1057 | } |
1058 | |||
1059 | static int preop_op(const char *inval, char *outval) | ||
1060 | { | ||
1061 | if (outval) | ||
1062 | strcpy(outval, preop); | ||
957 | 1063 | ||
958 | if (strcmp(preop, "preop_none") == 0) { | 1064 | if (!inval) |
1065 | return 0; | ||
1066 | |||
1067 | if (strcmp(inval, "preop_none") == 0) | ||
959 | preop_val = WDOG_PREOP_NONE; | 1068 | preop_val = WDOG_PREOP_NONE; |
960 | } else if (strcmp(preop, "preop_panic") == 0) { | 1069 | else if (strcmp(inval, "preop_panic") == 0) |
961 | preop_val = WDOG_PREOP_PANIC; | 1070 | preop_val = WDOG_PREOP_PANIC; |
962 | } else if (strcmp(preop, "preop_give_data") == 0) { | 1071 | else if (strcmp(inval, "preop_give_data") == 0) |
963 | preop_val = WDOG_PREOP_GIVE_DATA; | 1072 | preop_val = WDOG_PREOP_GIVE_DATA; |
964 | } else { | 1073 | else |
965 | preop_val = WDOG_PREOP_NONE; | 1074 | return -EINVAL; |
966 | printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" | 1075 | strcpy(preop, inval); |
967 | " none\n", preop); | 1076 | return 0; |
968 | } | 1077 | } |
969 | 1078 | ||
1079 | static void check_parms(void) | ||
1080 | { | ||
970 | #ifdef HAVE_NMI_HANDLER | 1081 | #ifdef HAVE_NMI_HANDLER |
1082 | int do_nmi = 0; | ||
1083 | int rv; | ||
1084 | |||
971 | if (preaction_val == WDOG_PRETIMEOUT_NMI) { | 1085 | if (preaction_val == WDOG_PRETIMEOUT_NMI) { |
1086 | do_nmi = 1; | ||
972 | if (preop_val == WDOG_PREOP_GIVE_DATA) { | 1087 | if (preop_val == WDOG_PREOP_GIVE_DATA) { |
973 | printk(KERN_WARNING PFX "Pretimeout op is to give data" | 1088 | printk(KERN_WARNING PFX "Pretimeout op is to give data" |
974 | " but NMI pretimeout is enabled, setting" | 1089 | " but NMI pretimeout is enabled, setting" |
975 | " pretimeout op to none\n"); | 1090 | " pretimeout op to none\n"); |
976 | preop_val = WDOG_PREOP_NONE; | 1091 | preop_op("preop_none", NULL); |
1092 | do_nmi = 0; | ||
977 | } | 1093 | } |
978 | #ifdef CONFIG_X86_LOCAL_APIC | 1094 | #ifdef CONFIG_X86_LOCAL_APIC |
979 | if (nmi_watchdog == NMI_IO_APIC) { | 1095 | if (nmi_watchdog == NMI_IO_APIC) { |
@@ -983,18 +1099,48 @@ static int __init ipmi_wdog_init(void) | |||
983 | " Disabling IPMI nmi pretimeout.\n", | 1099 | " Disabling IPMI nmi pretimeout.\n", |
984 | nmi_watchdog); | 1100 | nmi_watchdog); |
985 | preaction_val = WDOG_PRETIMEOUT_NONE; | 1101 | preaction_val = WDOG_PRETIMEOUT_NONE; |
986 | } else { | 1102 | do_nmi = 0; |
1103 | } | ||
987 | #endif | 1104 | #endif |
1105 | } | ||
1106 | if (do_nmi && !nmi_handler_registered) { | ||
988 | rv = request_nmi(&ipmi_nmi_handler); | 1107 | rv = request_nmi(&ipmi_nmi_handler); |
989 | if (rv) { | 1108 | if (rv) { |
990 | printk(KERN_WARNING PFX "Can't register nmi handler\n"); | 1109 | printk(KERN_WARNING PFX |
991 | return rv; | 1110 | "Can't register nmi handler\n"); |
992 | } | 1111 | return; |
993 | #ifdef CONFIG_X86_LOCAL_APIC | 1112 | } else |
994 | } | 1113 | nmi_handler_registered = 1; |
995 | #endif | 1114 | } else if (!do_nmi && nmi_handler_registered) { |
1115 | release_nmi(&ipmi_nmi_handler); | ||
1116 | nmi_handler_registered = 0; | ||
996 | } | 1117 | } |
997 | #endif | 1118 | #endif |
1119 | } | ||
1120 | |||
1121 | static int __init ipmi_wdog_init(void) | ||
1122 | { | ||
1123 | int rv; | ||
1124 | |||
1125 | if (action_op(action, NULL)) { | ||
1126 | action_op("reset", NULL); | ||
1127 | printk(KERN_INFO PFX "Unknown action '%s', defaulting to" | ||
1128 | " reset\n", action); | ||
1129 | } | ||
1130 | |||
1131 | if (preaction_op(preaction, NULL)) { | ||
1132 | preaction_op("pre_none", NULL); | ||
1133 | printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" | ||
1134 | " none\n", preaction); | ||
1135 | } | ||
1136 | |||
1137 | if (preop_op(preop, NULL)) { | ||
1138 | preop_op("preop_none", NULL); | ||
1139 | printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" | ||
1140 | " none\n", preop); | ||
1141 | } | ||
1142 | |||
1143 | check_parms(); | ||
998 | 1144 | ||
999 | rv = ipmi_smi_watcher_register(&smi_watcher); | 1145 | rv = ipmi_smi_watcher_register(&smi_watcher); |
1000 | if (rv) { | 1146 | if (rv) { |
@@ -1021,7 +1167,7 @@ static __exit void ipmi_unregister_watchdog(void) | |||
1021 | down_write(®ister_sem); | 1167 | down_write(®ister_sem); |
1022 | 1168 | ||
1023 | #ifdef HAVE_NMI_HANDLER | 1169 | #ifdef HAVE_NMI_HANDLER |
1024 | if (preaction_val == WDOG_PRETIMEOUT_NMI) | 1170 | if (nmi_handler_registered) |
1025 | release_nmi(&ipmi_nmi_handler); | 1171 | release_nmi(&ipmi_nmi_handler); |
1026 | #endif | 1172 | #endif |
1027 | 1173 | ||