summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSlava Pestov <sp@daterainc.com>2014-07-15 16:49:55 -0700
committerKent Overstreet <kmo@daterainc.com>2015-01-26 14:40:38 -0800
commitad82c9a399c14be27972a6f44608e523383eb18f (patch)
treedec73d7a5d4bbce562735af8c3fbc37313d97a10
parentdd9b387f19a7c7e7cbbccf5e3d1ce3b4d6fde65a (diff)
dynamic faults: allow referencing faults by ordinal
Sometimes we want to enable the first fault only, or the second fault. Add an 'index' directive to the query string format for this. Faults are numbered starting from 1; 0 means don't care. Also, writes to /sys/kernel/debug/dynamic_fault/control will now fail with -EINVAL if the query did not match any faults. Otherwise test scripts have no indication that a fault they thought was enabled did not get enabled. Change-Id: I2fb4beecd75849b217db535929cb80317fa4d796
-rw-r--r--include/linux/dynamic_fault.h3
-rw-r--r--lib/dynamic_fault.c118
2 files changed, 88 insertions, 33 deletions
diff --git a/include/linux/dynamic_fault.h b/include/linux/dynamic_fault.h
index c090547b39e5..850ebe1053ee 100644
--- a/include/linux/dynamic_fault.h
+++ b/include/linux/dynamic_fault.h
@@ -14,7 +14,8 @@ struct _dfault {
const char *modname;
const char *function;
const char *filename;
- unsigned int lineno:24;
+ unsigned int lineno:16;
+ unsigned int index:16;
/*
* The flags field controls the behaviour at the callsite.
* The bits here are changed dynamically when the user
diff --git a/lib/dynamic_fault.c b/lib/dynamic_fault.c
index b8668a5d33d0..d55cec90bf8f 100644
--- a/lib/dynamic_fault.c
+++ b/lib/dynamic_fault.c
@@ -1,7 +1,7 @@
/*
* lib/dynamic_fault.c
*
- * make fault() calls runtime configurable based upon their
+ * make dynamic_fault() calls runtime configurable based upon their
* source module.
*
* Copyright (C) 2011 Adam Berkan <aberkan@google.com>
@@ -55,6 +55,7 @@ struct dfault_query {
const char *module;
const char *function;
unsigned int first_lineno, last_lineno;
+ unsigned int first_index, last_index;
};
struct dfault_iter {
@@ -101,8 +102,8 @@ static char *dfault_describe_flags(struct _dfault *df, char *buf,
* the user which dfault's were changed, or whether none
* were matched.
*/
-static void dfault_change(const struct dfault_query *query,
- unsigned int flags, unsigned int mask)
+static int dfault_change(const struct dfault_query *query,
+ unsigned int flags, unsigned int mask)
{
int i;
struct dfault_table *dt;
@@ -141,6 +142,14 @@ static void dfault_change(const struct dfault_query *query,
df->lineno > query->last_lineno)
continue;
+ /* match against the fault index */
+ if (query->first_index &&
+ df->index < query->first_index)
+ continue;
+ if (query->last_index &&
+ df->index > query->last_index)
+ continue;
+
nfound++;
newflags = (df->flags & mask) | flags;
@@ -158,17 +167,21 @@ static void dfault_change(const struct dfault_query *query,
if (verbose)
printk(KERN_INFO
- "dfault: changed %s:%d [%s]%s %s\n",
- df->filename, df->lineno,
- dt->mod_name, df->function,
- dfault_describe_flags(df, flagbuf,
- sizeof(flagbuf)));
+ "dfault: changed %s:%d [%s]%s #%d %s\n",
+ df->filename, df->lineno,
+ dt->mod_name, df->function, df->index,
+ dfault_describe_flags(df, flagbuf,
+ sizeof(flagbuf)));
}
}
mutex_unlock(&dfault_lock);
- if (!nfound && verbose)
+ if (!nfound && verbose) {
printk(KERN_INFO "dfault: no matches for query\n");
+ return -ENOENT;
+ }
+
+ return 0;
}
/*
@@ -226,11 +239,11 @@ static int dfault_tokenize(char *buf, char *words[], int maxwords)
}
/*
- * Parse a single line number. Note that the empty string ""
- * is treated as a special case and converted to zero, which
+ * Parse a single number. Note that the empty string "" is
+ * treated as a special case and converted to zero, which
* is later treated as a "don't care" value.
*/
-static inline int parse_lineno(const char *str, unsigned int *val)
+static inline int parse_number(const char *str, unsigned int *val)
{
char *end = NULL;
BUG_ON(str == NULL);
@@ -243,6 +256,31 @@ static inline int parse_lineno(const char *str, unsigned int *val)
}
/*
+ * Parse a range.
+ */
+static inline int parse_range(char *str,
+ unsigned int *first,
+ unsigned int *last)
+{
+ char *first_str = str;
+ char *last_str = strchr(first_str, '-');
+
+ if (last_str)
+ *last_str++ = '\0';
+ if (parse_number(first_str, first) < 0)
+ return -EINVAL;
+ if (last_str != NULL) {
+ /* range <first>-<last> */
+ if (parse_number(last_str, last) < 0)
+ return -EINVAL;
+ } else {
+ *last = *first;
+ }
+
+ return 0;
+}
+
+/*
* Parse words[] as a dfault query specification, which is a series
* of (keyword, value) pairs chosen from these possibilities:
*
@@ -252,6 +290,8 @@ static inline int parse_lineno(const char *str, unsigned int *val)
* module <module-name>
* line <lineno>
* line <first-lineno>-<last-lineno> // where either may be empty
+ * index <m>-<n> // dynamic faults numbered from <m>
+ * // to <n> inside each matching function
*/
static int dfault_parse_query(char *words[], int nwords,
struct dfault_query *query)
@@ -271,19 +311,13 @@ static int dfault_parse_query(char *words[], int nwords,
else if (!strcmp(words[i], "module"))
query->module = words[i+1];
else if (!strcmp(words[i], "line")) {
- char *first = words[i+1];
- char *last = strchr(first, '-');
- if (last)
- *last++ = '\0';
- if (parse_lineno(first, &query->first_lineno) < 0)
- return -EINVAL;
- if (last != NULL) {
- /* range <first>-<last> */
- if (parse_lineno(last, &query->last_lineno) < 0)
- return -EINVAL;
- } else {
- query->last_lineno = query->first_lineno;
- }
+ parse_range(words[i+1],
+ &query->first_lineno,
+ &query->last_lineno);
+ } else if (!strcmp(words[i], "index")) {
+ parse_range(words[i+1],
+ &query->first_index,
+ &query->last_index);
} else {
if (verbose)
printk(KERN_ERR "%s: unknown keyword \"%s\"\n",
@@ -294,10 +328,11 @@ static int dfault_parse_query(char *words[], int nwords,
if (verbose)
printk(KERN_INFO "%s: q->function=\"%s\" q->filename=\"%s\" "
- "q->module=\"%s\" q->lineno=%u-%u\n",
+ "q->module=\"%s\" q->lineno=%u-%u\n q->index=%u-%u",
__func__, query->function, query->filename,
- query->module, query->first_lineno,
- query->last_lineno);
+ query->module,
+ query->first_lineno, query->last_lineno,
+ query->first_index, query->last_index);
return 0;
}
@@ -378,6 +413,7 @@ static ssize_t dfault_proc_write(struct file *file, const char __user *ubuf,
int nwords;
char *words[MAXWORDS];
char tmpbuf[256];
+ int ret;
if (len == 0)
return 0;
@@ -400,7 +436,9 @@ static ssize_t dfault_proc_write(struct file *file, const char __user *ubuf,
return -EINVAL;
/* actually go and implement the change */
- dfault_change(&query, flags, mask);
+ ret = dfault_change(&query, flags, mask);
+ if (ret < 0)
+ return ret;
*offp += len;
return len;
@@ -514,13 +552,14 @@ static int dfault_proc_show(struct seq_file *m, void *p)
if (p == SEQ_START_TOKEN) {
seq_puts(m,
- "# filename:lineno [module]function flags format\n");
+ "# filename:lineno [module]function index "
+ "flags format\n");
return 0;
}
- seq_printf(m, "%s:%u [%s]%s %s \"",
+ seq_printf(m, "%s:%u [%s]%s %d %s \"",
df->filename, df->lineno,
- iter->table->mod_name, df->function,
+ iter->table->mod_name, df->function, df->index,
dfault_describe_flags(df, flagsbuf, sizeof(flagsbuf)));
seq_puts(m, "\"\n");
@@ -591,6 +630,9 @@ int dfault_add_module(struct _dfault *tab, unsigned int n,
{
struct dfault_table *dt;
char *new_name;
+ const char *func;
+ unsigned index;
+ int i;
dt = kzalloc(sizeof(*dt), GFP_KERNEL);
if (dt == NULL)
@@ -609,6 +651,18 @@ int dfault_add_module(struct _dfault *tab, unsigned int n,
list_add_tail(&dt->link, &dfault_tables);
mutex_unlock(&dfault_lock);
+ index = 1;
+ func = NULL;
+
+ /* __attribute__(("section")) emits things in reverse order */
+ for (i = n - 1; i >= 0; i--) {
+ if (!func || strcmp(tab[i].function, func)) {
+ index = 1;
+ func = tab[i].function;
+ }
+ tab[i].index = index++;
+ }
+
if (verbose)
printk(KERN_INFO "%u debug prints in module %s\n",
n, dt->mod_name);