Skip to content

Commit

Permalink
of: Custom printk format specifier for device node
Browse files Browse the repository at this point in the history
90% of the usage of device node's full_name is printing it out
in a kernel message. Preparing for the eventual delayed allocation
introduce a custom printk format specifier that is both more
compact and more pleasant to the eye.

For instance typical use is:
	pr_info("Frobbing node %s\n", node->full_name);

Which can be written now as:
	pr_info("Frobbing node %pO\n", node);

More fine-grained control of formatting includes printing the name,
flag, path-spec name, reference count and others, explained in the
documentation entry.

Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
  • Loading branch information
pantoniou authored and RobertCNelson committed Jun 29, 2015
1 parent 09326f9 commit 5bb7d6f
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Documentation/printk-formats.txt
Expand Up @@ -284,6 +284,37 @@ bitmap and its derivatives such as cpumask and nodemask:

Passed by reference.

Device tree nodes:

%pO[fnpPcCFr]

For printing device tree nodes. The optional arguments are:
f device node full_name
n device node name
p device node phandle
P device node path spec (name + @unit)
F device node flags
c major compatible string
C full compatible string
r node reference count
Without any arguments prints full_name (same as %pOf)
The separator when using multiple arguments is '|'

Examples:

%pO /foo/bar@0 - Node full name
%pOf /foo/bar@0 - Same as above
%pOfp /foo/bar@0|10 - Node full name + phandle
%pOfcF /foo/bar@0|foo,device|--P- - Node full name +
major compatible string +
node flags
D - dynamic
d - detached
P - Populated
B - Populated bus

Passed by reference

Thank you for your cooperation and attention.


Expand Down
148 changes: 148 additions & 0 deletions lib/vsprintf.c
Expand Up @@ -30,6 +30,7 @@
#include <linux/dcache.h>
#include <linux/cred.h>
#include <net/addrconf.h>
#include <linux/of.h>

#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/sections.h> /* for dereference_function_descriptor() */
Expand Down Expand Up @@ -1360,6 +1361,141 @@ char *clock(char *buf, char *end, struct clk *clk, struct printf_spec spec,
}
}

/* helper method for calculating extends on first pass and filling in later */
static noinline_for_stack
void append_str(const char *str, int pass, int *lenp, char **bufp, char *end)
{
int len;

len = strlen(str);
if (pass == 1)
*lenp += len;
else {
if (len > (end - *bufp))
len = end - *bufp;
memcpy(*bufp, str, len);
*bufp += len;
}
}

static noinline_for_stack
char *device_node_string(char *buf, char *end, struct device_node *dn,
struct printf_spec spec, const char *fmt)
{
char tbuf[sizeof("xxxxxxxxxx") + 1];
const char *fmtp, *p;
int len, ret, i, j, pass;
char c;

if (!IS_ENABLED(CONFIG_OF)) {
/* if OF is not enabled just print the pointer */
spec.flags |= SMALL;
if (spec.field_width == -1) {
spec.field_width = 2 * sizeof(void *);
spec.flags |= ZEROPAD;
}
spec.base = 16;
return number(buf, end, (unsigned long) dn, spec);
}

if ((unsigned long)dn < PAGE_SIZE)
return string(buf, end, "(null)", spec);

/* simple case without anything any more format specifiers */
if (fmt[1] == '\0' || isspace(fmt[1]))
fmt = "Of";

len = 0;

/* two passes; the first calculates length, the second fills in */
for (pass = 1; pass <= 2; pass++) {
if (pass == 2 && !(spec.flags & LEFT)) {
/* padding */
while (len < spec.field_width--) {
if (buf < end)
*buf = ' ';
++buf;
}
}

for (fmtp = fmt + 1, j = 0; (c = *fmtp++) != '\0'; ) {

/* validate option */
if (c != 'f' && c != 'n' && c != 'p' && c != 'P' &&
c != 'F' && c != 'c' && c != 'C' && c != 'r')
continue;

/* handle separator */
if (j++ > 0)
append_str("|", pass, &len, &buf, end);

switch (c) {
case 'f': /* full_name */
append_str(of_node_full_name(dn), pass, &len,
&buf, end);
break;
case 'n': /* name */
append_str(dn->name, pass, &len, &buf, end);
break;
case 'p': /* phandle */
snprintf(tbuf, sizeof(tbuf), "%u",
(unsigned int)dn->phandle);
append_str(tbuf, pass, &len, &buf, end);
break;
case 'P': /* path-spec */
append_str(dn->name, pass, &len, &buf, end);
/* need to tack on the @ postfix */
p = strchr(of_node_full_name(dn), '@');
if (p)
append_str(p, pass, &len, &buf, end);
break;
case 'F': /* flags */
snprintf(tbuf, sizeof(tbuf), "%c%c%c%c",
of_node_check_flag(dn, OF_DYNAMIC) ?
'D' : '-',
of_node_check_flag(dn, OF_DETACHED) ?
'd' : '-',
of_node_check_flag(dn, OF_POPULATED) ?
'P' : '-',
of_node_check_flag(dn,
OF_POPULATED_BUS) ? 'B' : '-');
append_str(tbuf, pass, &len, &buf, end);
break;
case 'c': /* major compatible string */
ret = of_property_read_string(dn, "compatible",
&p);
if (ret == 0)
append_str(p, pass, &len, &buf, end);
break;
case 'C': /* full compatible string */
i = 0;
while (of_property_read_string_index(dn,
"compatible", i, &p) == 0) {
append_str(i == 0 ? "\"" : "\",\"",
pass, &len, &buf, end);
append_str(p, pass, &len, &buf, end);
i++;
}
if (i > 0)
append_str("\"", pass, &len, &buf, end);
break;
case 'r': /* node reference count */
snprintf(tbuf, sizeof(tbuf), "%u",
atomic_read(&dn->kobj.kref.refcount));
append_str(tbuf, pass, &len, &buf, end);
break;
default:
break;
}
}
}
/* finish up */
while (buf < end && len < spec.field_width--)
*buf++ = ' ';

return buf;
}

int kptr_restrict __read_mostly;

/*
Expand Down Expand Up @@ -1447,6 +1583,16 @@ int kptr_restrict __read_mostly;
* - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
* (legacy clock framework) of the clock
* - 'Cr' For a clock, it prints the current rate of the clock
* - 'O[fnpPcCFr]' For an DT device node
* Without any optional arguments prints the full_name
* f device node full_name
* n device node name
* p device node phandle
* P device node path spec (name + @unit)
* F device node flags
* c major compatible string
* C full compatible string
* r node reference count
*
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
Expand Down Expand Up @@ -1597,6 +1743,8 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
return dentry_name(buf, end,
((const struct file *)ptr)->f_path.dentry,
spec, fmt);
case 'O':
return device_node_string(buf, end, ptr, spec, fmt);
}
spec.flags |= SMALL;
if (spec.field_width == -1) {
Expand Down

0 comments on commit 5bb7d6f

Please sign in to comment.