|
17 | 17 | #include <linux/sched.h>
|
18 | 18 | #include <linux/sched/debug.h>
|
19 | 19 | #include <linux/slab.h>
|
| 20 | +#include <linux/string.h> |
20 | 21 | #include <linux/clocksource.h>
|
21 | 22 |
|
22 | 23 | #include <asm/apic.h>
|
@@ -178,65 +179,67 @@ module_param_named(debug, uv_nmi_debug, int, 0644);
|
178 | 179 | } while (0)
|
179 | 180 |
|
180 | 181 | /* Valid NMI Actions */
|
181 |
| -#define ACTION_LEN 16 |
182 |
| -static struct nmi_action { |
183 |
| - char *action; |
184 |
| - char *desc; |
185 |
| -} valid_acts[] = { |
186 |
| - { "kdump", "do kernel crash dump" }, |
187 |
| - { "dump", "dump process stack for each cpu" }, |
188 |
| - { "ips", "dump Inst Ptr info for each cpu" }, |
189 |
| - { "kdb", "enter KDB (needs kgdboc= assignment)" }, |
190 |
| - { "kgdb", "enter KGDB (needs gdb target remote)" }, |
191 |
| - { "health", "check if CPUs respond to NMI" }, |
| 182 | +enum action_t { |
| 183 | + nmi_act_kdump, |
| 184 | + nmi_act_dump, |
| 185 | + nmi_act_ips, |
| 186 | + nmi_act_kdb, |
| 187 | + nmi_act_kgdb, |
| 188 | + nmi_act_health, |
| 189 | + nmi_act_max |
192 | 190 | };
|
193 |
| -typedef char action_t[ACTION_LEN]; |
194 |
| -static action_t uv_nmi_action = { "dump" }; |
| 191 | + |
| 192 | +static const char * const actions[nmi_act_max] = { |
| 193 | + [nmi_act_kdump] = "kdump", |
| 194 | + [nmi_act_dump] = "dump", |
| 195 | + [nmi_act_ips] = "ips", |
| 196 | + [nmi_act_kdb] = "kdb", |
| 197 | + [nmi_act_kgdb] = "kgdb", |
| 198 | + [nmi_act_health] = "health", |
| 199 | +}; |
| 200 | + |
| 201 | +static const char * const actions_desc[nmi_act_max] = { |
| 202 | + [nmi_act_kdump] = "do kernel crash dump", |
| 203 | + [nmi_act_dump] = "dump process stack for each cpu", |
| 204 | + [nmi_act_ips] = "dump Inst Ptr info for each cpu", |
| 205 | + [nmi_act_kdb] = "enter KDB (needs kgdboc= assignment)", |
| 206 | + [nmi_act_kgdb] = "enter KGDB (needs gdb target remote)", |
| 207 | + [nmi_act_health] = "check if CPUs respond to NMI", |
| 208 | +}; |
| 209 | + |
| 210 | +static enum action_t uv_nmi_action = nmi_act_dump; |
195 | 211 |
|
196 | 212 | static int param_get_action(char *buffer, const struct kernel_param *kp)
|
197 | 213 | {
|
198 |
| - return sprintf(buffer, "%s\n", uv_nmi_action); |
| 214 | + return sprintf(buffer, "%s\n", actions[uv_nmi_action]); |
199 | 215 | }
|
200 | 216 |
|
201 | 217 | static int param_set_action(const char *val, const struct kernel_param *kp)
|
202 | 218 | {
|
203 |
| - int i; |
204 |
| - int n = ARRAY_SIZE(valid_acts); |
205 |
| - char arg[ACTION_LEN]; |
206 |
| - |
207 |
| - /* (remove possible '\n') */ |
208 |
| - strscpy(arg, val, strnchrnul(val, sizeof(arg)-1, '\n') - val + 1); |
209 |
| - |
210 |
| - for (i = 0; i < n; i++) |
211 |
| - if (!strcmp(arg, valid_acts[i].action)) |
212 |
| - break; |
| 219 | + int i, n = ARRAY_SIZE(actions); |
213 | 220 |
|
214 |
| - if (i < n) { |
215 |
| - strscpy(uv_nmi_action, arg, sizeof(uv_nmi_action)); |
216 |
| - pr_info("UV: New NMI action:%s\n", uv_nmi_action); |
| 221 | + i = sysfs_match_string(actions, val); |
| 222 | + if (i >= 0) { |
| 223 | + uv_nmi_action = i; |
| 224 | + pr_info("UV: New NMI action:%s\n", actions[i]); |
217 | 225 | return 0;
|
218 | 226 | }
|
219 | 227 |
|
220 |
| - pr_err("UV: Invalid NMI action:%s, valid actions are:\n", arg); |
| 228 | + pr_err("UV: Invalid NMI action. Valid actions are:\n"); |
221 | 229 | for (i = 0; i < n; i++)
|
222 |
| - pr_err("UV: %-8s - %s\n", |
223 |
| - valid_acts[i].action, valid_acts[i].desc); |
| 230 | + pr_err("UV: %-8s - %s\n", actions[i], actions_desc[i]); |
| 231 | + |
224 | 232 | return -EINVAL;
|
225 | 233 | }
|
226 | 234 |
|
227 | 235 | static const struct kernel_param_ops param_ops_action = {
|
228 | 236 | .get = param_get_action,
|
229 | 237 | .set = param_set_action,
|
230 | 238 | };
|
231 |
| -#define param_check_action(name, p) __param_check(name, p, action_t) |
| 239 | +#define param_check_action(name, p) __param_check(name, p, enum action_t) |
232 | 240 |
|
233 | 241 | module_param_named(action, uv_nmi_action, action, 0644);
|
234 | 242 |
|
235 |
| -static inline bool uv_nmi_action_is(const char *action) |
236 |
| -{ |
237 |
| - return (strncmp(uv_nmi_action, action, strlen(action)) == 0); |
238 |
| -} |
239 |
| - |
240 | 243 | /* Setup which NMI support is present in system */
|
241 | 244 | static void uv_nmi_setup_mmrs(void)
|
242 | 245 | {
|
@@ -727,10 +730,10 @@ static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs)
|
727 | 730 | if (cpu == 0)
|
728 | 731 | uv_nmi_dump_cpu_ip_hdr();
|
729 | 732 |
|
730 |
| - if (current->pid != 0 || !uv_nmi_action_is("ips")) |
| 733 | + if (current->pid != 0 || uv_nmi_action != nmi_act_ips) |
731 | 734 | uv_nmi_dump_cpu_ip(cpu, regs);
|
732 | 735 |
|
733 |
| - if (uv_nmi_action_is("dump")) { |
| 736 | + if (uv_nmi_action == nmi_act_dump) { |
734 | 737 | pr_info("UV:%sNMI process trace for CPU %d\n", dots, cpu);
|
735 | 738 | show_regs(regs);
|
736 | 739 | }
|
@@ -798,7 +801,7 @@ static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master)
|
798 | 801 | int saved_console_loglevel = console_loglevel;
|
799 | 802 |
|
800 | 803 | pr_alert("UV: tracing %s for %d CPUs from CPU %d\n",
|
801 |
| - uv_nmi_action_is("ips") ? "IPs" : "processes", |
| 804 | + uv_nmi_action == nmi_act_ips ? "IPs" : "processes", |
802 | 805 | atomic_read(&uv_nmi_cpus_in_nmi), cpu);
|
803 | 806 |
|
804 | 807 | console_loglevel = uv_nmi_loglevel;
|
@@ -874,7 +877,7 @@ static inline int uv_nmi_kdb_reason(void)
|
874 | 877 | static inline int uv_nmi_kdb_reason(void)
|
875 | 878 | {
|
876 | 879 | /* Ensure user is expecting to attach gdb remote */
|
877 |
| - if (uv_nmi_action_is("kgdb")) |
| 880 | + if (uv_nmi_action == nmi_act_kgdb) |
878 | 881 | return 0;
|
879 | 882 |
|
880 | 883 | pr_err("UV: NMI error: KDB is not enabled in this kernel\n");
|
@@ -950,28 +953,35 @@ static int uv_handle_nmi(unsigned int reason, struct pt_regs *regs)
|
950 | 953 | master = (atomic_read(&uv_nmi_cpu) == cpu);
|
951 | 954 |
|
952 | 955 | /* If NMI action is "kdump", then attempt to do it */
|
953 |
| - if (uv_nmi_action_is("kdump")) { |
| 956 | + if (uv_nmi_action == nmi_act_kdump) { |
954 | 957 | uv_nmi_kdump(cpu, master, regs);
|
955 | 958 |
|
956 | 959 | /* Unexpected return, revert action to "dump" */
|
957 | 960 | if (master)
|
958 |
| - strscpy(uv_nmi_action, "dump", sizeof(uv_nmi_action)); |
| 961 | + uv_nmi_action = nmi_act_dump; |
959 | 962 | }
|
960 | 963 |
|
961 | 964 | /* Pause as all CPU's enter the NMI handler */
|
962 | 965 | uv_nmi_wait(master);
|
963 | 966 |
|
964 | 967 | /* Process actions other than "kdump": */
|
965 |
| - if (uv_nmi_action_is("health")) { |
| 968 | + switch (uv_nmi_action) { |
| 969 | + case nmi_act_health: |
966 | 970 | uv_nmi_action_health(cpu, regs, master);
|
967 |
| - } else if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) { |
| 971 | + break; |
| 972 | + case nmi_act_ips: |
| 973 | + case nmi_act_dump: |
968 | 974 | uv_nmi_dump_state(cpu, regs, master);
|
969 |
| - } else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) { |
| 975 | + break; |
| 976 | + case nmi_act_kdb: |
| 977 | + case nmi_act_kgdb: |
970 | 978 | uv_call_kgdb_kdb(cpu, regs, master);
|
971 |
| - } else { |
| 979 | + break; |
| 980 | + default: |
972 | 981 | if (master)
|
973 |
| - pr_alert("UV: unknown NMI action: %s\n", uv_nmi_action); |
| 982 | + pr_alert("UV: unknown NMI action: %d\n", uv_nmi_action); |
974 | 983 | uv_nmi_sync_exit(master);
|
| 984 | + break; |
975 | 985 | }
|
976 | 986 |
|
977 | 987 | /* Clear per_cpu "in_nmi" flag */
|
|
0 commit comments