Merge tag 'efi-urgent' into x86/urgent

* Avoid WARN_ON() when mapping BGRT on Baytrail (EFI 32-bit).

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
This commit is contained in:
H. Peter Anvin
2014-02-07 11:27:30 -08:00
9081 changed files with 515598 additions and 199847 deletions

View File

@@ -1556,17 +1556,6 @@ config PROVIDE_OHCI1394_DMA_INIT
See Documentation/debugging-via-ohci1394.txt for more information.
config FIREWIRE_OHCI_REMOTE_DMA
bool "Remote debugging over FireWire with firewire-ohci"
depends on FIREWIRE_OHCI
help
This option lets you use the FireWire bus for remote debugging
with help of the firewire-ohci driver. It enables unfiltered
remote DMA in firewire-ohci.
See Documentation/debugging-via-ohci1394.txt for more information.
If unsure, say N.
config BUILD_DOCSRC
bool "Build targets in Documentation/ tree"
depends on HEADERS_CHECK
@@ -1584,8 +1573,43 @@ config DMA_API_DEBUG
With this option you will be able to detect common bugs in device
drivers like double-freeing of DMA mappings or freeing mappings that
were never allocated.
This option causes a performance degredation. Use only if you want
to debug device drivers. If unsure, say N.
This also attempts to catch cases where a page owned by DMA is
accessed by the cpu in a way that could cause data corruption. For
example, this enables cow_user_page() to check that the source page is
not undergoing DMA.
This option causes a performance degradation. Use only if you want to
debug device drivers and dma interactions.
If unsure, say N.
config TEST_MODULE
tristate "Test module loading with 'hello world' module"
default n
depends on m
help
This builds the "test_module" module that emits "Hello, world"
on printk when loaded. It is designed to be used for basic
evaluation of the module loading subsystem (for example when
validating module verification). It lacks any extra dependencies,
and will not normally be loaded by the system unless explicitly
requested by name.
If unsure, say N.
config TEST_USER_COPY
tristate "Test user/kernel boundary protections"
default n
depends on m
help
This builds the "test_user_copy" module that runs sanity checks
on the copy_to/from_user infrastructure, making sure basic
user/kernel boundary testing is working. If it fails to load,
a regression has been detected in the user/kernel memory boundary
protections.
If unsure, say N.
source "samples/Kconfig"

View File

@@ -26,11 +26,13 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
percpu-refcount.o percpu_ida.o
percpu-refcount.o percpu_ida.o hash.o
obj-y += string_helpers.o
obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
obj-y += kstrtox.o
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
obj-$(CONFIG_TEST_MODULE) += test_module.o
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
CFLAGS_kobject.o += -DDEBUG

View File

@@ -157,7 +157,7 @@ enum assoc_array_walk_status {
assoc_array_walk_tree_empty,
assoc_array_walk_found_terminal_node,
assoc_array_walk_found_wrong_shortcut,
} status;
};
struct assoc_array_walk_result {
struct {

View File

@@ -53,8 +53,10 @@ EXPORT_SYMBOL(ewma_init);
*/
struct ewma *ewma_add(struct ewma *avg, unsigned long val)
{
avg->internal = avg->internal ?
(((avg->internal << avg->weight) - avg->internal) +
unsigned long internal = ACCESS_ONCE(avg->internal);
ACCESS_ONCE(avg->internal) = internal ?
(((internal << avg->weight) - internal) +
(val << avg->factor)) >> avg->weight :
(val << avg->factor);
return avg;

View File

@@ -49,13 +49,13 @@ static int get_range(char **str, int *pint)
* 3 - hyphen found to denote a range
*/
int get_option (char **str, int *pint)
int get_option(char **str, int *pint)
{
char *cur = *str;
if (!cur || !(*cur))
return 0;
*pint = simple_strtol (cur, str, 0);
*pint = simple_strtol(cur, str, 0);
if (cur == *str)
return 0;
if (**str == ',') {
@@ -67,6 +67,7 @@ int get_option (char **str, int *pint)
return 1;
}
EXPORT_SYMBOL(get_option);
/**
* get_options - Parse a string into a list of integers
@@ -84,13 +85,13 @@ int get_option (char **str, int *pint)
* the parse to end (typically a null terminator, if @str is
* completely parseable).
*/
char *get_options(const char *str, int nints, int *ints)
{
int res, i = 1;
while (i < nints) {
res = get_option ((char **)&str, ints + i);
res = get_option((char **)&str, ints + i);
if (res == 0)
break;
if (res == 3) {
@@ -112,6 +113,7 @@ char *get_options(const char *str, int nints, int *ints)
ints[0] = i - 1;
return (char *)str;
}
EXPORT_SYMBOL(get_options);
/**
* memparse - parse a string with mem suffixes into a number
@@ -152,8 +154,4 @@ unsigned long long memparse(const char *ptr, char **retptr)
return ret;
}
EXPORT_SYMBOL(memparse);
EXPORT_SYMBOL(get_option);
EXPORT_SYMBOL(get_options);

View File

@@ -140,7 +140,7 @@ EXPORT_SYMBOL(zalloc_cpumask_var);
*/
void __init alloc_bootmem_cpumask_var(cpumask_var_t *mask)
{
*mask = alloc_bootmem(cpumask_size());
*mask = memblock_virt_alloc(cpumask_size(), 0);
}
/**
@@ -161,6 +161,6 @@ EXPORT_SYMBOL(free_cpumask_var);
*/
void __init free_bootmem_cpumask_var(cpumask_var_t mask)
{
free_bootmem(__pa(mask), cpumask_size());
memblock_free_early(__pa(mask), cpumask_size());
}
#endif

View File

@@ -141,6 +141,7 @@ STATIC inline int INIT unlz4(u8 *input, int in_len,
goto exit_2;
}
ret = -1;
if (flush && flush(outp, dest_len) != dest_len)
goto exit_2;
if (output)

View File

@@ -53,11 +53,26 @@ enum map_err_types {
#define DMA_DEBUG_STACKTRACE_ENTRIES 5
/**
* struct dma_debug_entry - track a dma_map* or dma_alloc_coherent mapping
* @list: node on pre-allocated free_entries list
* @dev: 'dev' argument to dma_map_{page|single|sg} or dma_alloc_coherent
* @type: single, page, sg, coherent
* @pfn: page frame of the start address
* @offset: offset of mapping relative to pfn
* @size: length of the mapping
* @direction: enum dma_data_direction
* @sg_call_ents: 'nents' from dma_map_sg
* @sg_mapped_ents: 'mapped_ents' from dma_map_sg
* @map_err_type: track whether dma_mapping_error() was checked
* @stacktrace: support backtraces when a violation is detected
*/
struct dma_debug_entry {
struct list_head list;
struct device *dev;
int type;
phys_addr_t paddr;
unsigned long pfn;
size_t offset;
u64 dev_addr;
u64 size;
int direction;
@@ -372,6 +387,11 @@ static void hash_bucket_del(struct dma_debug_entry *entry)
list_del(&entry->list);
}
static unsigned long long phys_addr(struct dma_debug_entry *entry)
{
return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset;
}
/*
* Dump mapping entries for debugging purposes
*/
@@ -389,9 +409,9 @@ void debug_dma_dump_mappings(struct device *dev)
list_for_each_entry(entry, &bucket->list, list) {
if (!dev || dev == entry->dev) {
dev_info(entry->dev,
"%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n",
"%s idx %d P=%Lx N=%lx D=%Lx L=%Lx %s %s\n",
type2name[entry->type], idx,
(unsigned long long)entry->paddr,
phys_addr(entry), entry->pfn,
entry->dev_addr, entry->size,
dir2name[entry->direction],
maperr2str[entry->map_err_type]);
@@ -403,6 +423,137 @@ void debug_dma_dump_mappings(struct device *dev)
}
EXPORT_SYMBOL(debug_dma_dump_mappings);
/*
* For each page mapped (initial page in the case of
* dma_alloc_coherent/dma_map_{single|page}, or each page in a
* scatterlist) insert into this tree using the pfn as the key. At
* dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If
* the pfn already exists at insertion time add a tag as a reference
* count for the overlapping mappings. For now, the overlap tracking
* just ensures that 'unmaps' balance 'maps' before marking the pfn
* idle, but we should also be flagging overlaps as an API violation.
*
* Memory usage is mostly constrained by the maximum number of available
* dma-debug entries in that we need a free dma_debug_entry before
* inserting into the tree. In the case of dma_map_{single|page} and
* dma_alloc_coherent there is only one dma_debug_entry and one pfn to
* track per event. dma_map_sg(), on the other hand,
* consumes a single dma_debug_entry, but inserts 'nents' entries into
* the tree.
*
* At any time debug_dma_assert_idle() can be called to trigger a
* warning if the given page is in the active set.
*/
static RADIX_TREE(dma_active_pfn, GFP_NOWAIT);
static DEFINE_SPINLOCK(radix_lock);
#define ACTIVE_PFN_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1)
static int active_pfn_read_overlap(unsigned long pfn)
{
int overlap = 0, i;
for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (radix_tree_tag_get(&dma_active_pfn, pfn, i))
overlap |= 1 << i;
return overlap;
}
static int active_pfn_set_overlap(unsigned long pfn, int overlap)
{
int i;
if (overlap > ACTIVE_PFN_MAX_OVERLAP || overlap < 0)
return overlap;
for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (overlap & 1 << i)
radix_tree_tag_set(&dma_active_pfn, pfn, i);
else
radix_tree_tag_clear(&dma_active_pfn, pfn, i);
return overlap;
}
static void active_pfn_inc_overlap(unsigned long pfn)
{
int overlap = active_pfn_read_overlap(pfn);
overlap = active_pfn_set_overlap(pfn, ++overlap);
/* If we overflowed the overlap counter then we're potentially
* leaking dma-mappings. Otherwise, if maps and unmaps are
* balanced then this overflow may cause false negatives in
* debug_dma_assert_idle() as the pfn may be marked idle
* prematurely.
*/
WARN_ONCE(overlap > ACTIVE_PFN_MAX_OVERLAP,
"DMA-API: exceeded %d overlapping mappings of pfn %lx\n",
ACTIVE_PFN_MAX_OVERLAP, pfn);
}
static int active_pfn_dec_overlap(unsigned long pfn)
{
int overlap = active_pfn_read_overlap(pfn);
return active_pfn_set_overlap(pfn, --overlap);
}
static int active_pfn_insert(struct dma_debug_entry *entry)
{
unsigned long flags;
int rc;
spin_lock_irqsave(&radix_lock, flags);
rc = radix_tree_insert(&dma_active_pfn, entry->pfn, entry);
if (rc == -EEXIST)
active_pfn_inc_overlap(entry->pfn);
spin_unlock_irqrestore(&radix_lock, flags);
return rc;
}
static void active_pfn_remove(struct dma_debug_entry *entry)
{
unsigned long flags;
spin_lock_irqsave(&radix_lock, flags);
/* since we are counting overlaps the final put of the
* entry->pfn will occur when the overlap count is 0.
* active_pfn_dec_overlap() returns -1 in that case
*/
if (active_pfn_dec_overlap(entry->pfn) < 0)
radix_tree_delete(&dma_active_pfn, entry->pfn);
spin_unlock_irqrestore(&radix_lock, flags);
}
/**
* debug_dma_assert_idle() - assert that a page is not undergoing dma
* @page: page to lookup in the dma_active_pfn tree
*
* Place a call to this routine in cases where the cpu touching the page
* before the dma completes (page is dma_unmapped) will lead to data
* corruption.
*/
void debug_dma_assert_idle(struct page *page)
{
unsigned long flags;
struct dma_debug_entry *entry;
if (!page)
return;
spin_lock_irqsave(&radix_lock, flags);
entry = radix_tree_lookup(&dma_active_pfn, page_to_pfn(page));
spin_unlock_irqrestore(&radix_lock, flags);
if (!entry)
return;
err_printk(entry->dev, entry,
"DMA-API: cpu touching an active dma mapped page "
"[pfn=0x%lx]\n", entry->pfn);
}
/*
* Wrapper function for adding an entry to the hash.
* This function takes care of locking itself.
@@ -411,10 +562,21 @@ static void add_dma_entry(struct dma_debug_entry *entry)
{
struct hash_bucket *bucket;
unsigned long flags;
int rc;
bucket = get_hash_bucket(entry, &flags);
hash_bucket_add(bucket, entry);
put_hash_bucket(bucket, &flags);
rc = active_pfn_insert(entry);
if (rc == -ENOMEM) {
pr_err("DMA-API: pfn tracking ENOMEM, dma-debug disabled\n");
global_disable = true;
}
/* TODO: report -EEXIST errors here as overlapping mappings are
* not supported by the DMA API
*/
}
static struct dma_debug_entry *__dma_entry_alloc(void)
@@ -469,6 +631,8 @@ static void dma_entry_free(struct dma_debug_entry *entry)
{
unsigned long flags;
active_pfn_remove(entry);
/*
* add to beginning of the list - this way the entries are
* more likely cache hot when they are reallocated.
@@ -895,15 +1059,15 @@ static void check_unmap(struct dma_debug_entry *ref)
ref->dev_addr, ref->size,
type2name[entry->type], type2name[ref->type]);
} else if ((entry->type == dma_debug_coherent) &&
(ref->paddr != entry->paddr)) {
(phys_addr(ref) != phys_addr(entry))) {
err_printk(ref->dev, entry, "DMA-API: device driver frees "
"DMA memory with different CPU address "
"[device address=0x%016llx] [size=%llu bytes] "
"[cpu alloc address=0x%016llx] "
"[cpu free address=0x%016llx]",
ref->dev_addr, ref->size,
(unsigned long long)entry->paddr,
(unsigned long long)ref->paddr);
phys_addr(entry),
phys_addr(ref));
}
if (ref->sg_call_ents && ref->type == dma_debug_sg &&
@@ -1052,7 +1216,8 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
entry->dev = dev;
entry->type = dma_debug_page;
entry->paddr = page_to_phys(page) + offset;
entry->pfn = page_to_pfn(page);
entry->offset = offset,
entry->dev_addr = dma_addr;
entry->size = size;
entry->direction = direction;
@@ -1148,7 +1313,8 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
entry->type = dma_debug_sg;
entry->dev = dev;
entry->paddr = sg_phys(s);
entry->pfn = page_to_pfn(sg_page(s));
entry->offset = s->offset,
entry->size = sg_dma_len(s);
entry->dev_addr = sg_dma_address(s);
entry->direction = direction;
@@ -1198,7 +1364,8 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.paddr = sg_phys(s),
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = dir,
@@ -1233,7 +1400,8 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
entry->type = dma_debug_coherent;
entry->dev = dev;
entry->paddr = virt_to_phys(virt);
entry->pfn = page_to_pfn(virt_to_page(virt));
entry->offset = (size_t) virt & PAGE_MASK;
entry->size = size;
entry->dev_addr = dma_addr;
entry->direction = DMA_BIDIRECTIONAL;
@@ -1248,7 +1416,8 @@ void debug_dma_free_coherent(struct device *dev, size_t size,
struct dma_debug_entry ref = {
.type = dma_debug_coherent,
.dev = dev,
.paddr = virt_to_phys(virt),
.pfn = page_to_pfn(virt_to_page(virt)),
.offset = (size_t) virt & PAGE_MASK,
.dev_addr = addr,
.size = size,
.direction = DMA_BIDIRECTIONAL,
@@ -1356,7 +1525,8 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.paddr = sg_phys(s),
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = direction,
@@ -1388,7 +1558,8 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
struct dma_debug_entry ref = {
.type = dma_debug_sg,
.dev = dev,
.paddr = sg_phys(s),
.pfn = page_to_pfn(sg_page(s)),
.offset = s->offset,
.dev_addr = sg_dma_address(s),
.size = sg_dma_len(s),
.direction = direction,

View File

@@ -8,6 +8,7 @@
* By Greg Banks <gnb@melbourne.sgi.com>
* Copyright (c) 2008 Silicon Graphics Inc. All Rights Reserved.
* Copyright (C) 2011 Bart Van Assche. All Rights Reserved.
* Copyright (C) 2013 Du, Changbin <changbin.du@gmail.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
@@ -24,6 +25,7 @@
#include <linux/sysctl.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/parser.h>
#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <linux/dynamic_debug.h>
@@ -147,7 +149,8 @@ static int ddebug_change(const struct ddebug_query *query,
list_for_each_entry(dt, &ddebug_tables, link) {
/* match against the module name */
if (query->module && strcmp(query->module, dt->mod_name))
if (query->module &&
!match_wildcard(query->module, dt->mod_name))
continue;
for (i = 0; i < dt->num_ddebugs; i++) {
@@ -155,14 +158,16 @@ static int ddebug_change(const struct ddebug_query *query,
/* match against the source filename */
if (query->filename &&
strcmp(query->filename, dp->filename) &&
strcmp(query->filename, kbasename(dp->filename)) &&
strcmp(query->filename, trim_prefix(dp->filename)))
!match_wildcard(query->filename, dp->filename) &&
!match_wildcard(query->filename,
kbasename(dp->filename)) &&
!match_wildcard(query->filename,
trim_prefix(dp->filename)))
continue;
/* match against the function */
if (query->function &&
strcmp(query->function, dp->function))
!match_wildcard(query->function, dp->function))
continue;
/* match against the format */
@@ -263,14 +268,12 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords)
*/
static inline int parse_lineno(const char *str, unsigned int *val)
{
char *end = NULL;
BUG_ON(str == NULL);
if (*str == '\0') {
*val = 0;
return 0;
}
*val = simple_strtoul(str, &end, 10);
if (end == NULL || end == str || *end != '\0') {
if (kstrtouint(str, 10, val) < 0) {
pr_err("bad line-number: %s\n", str);
return -EINVAL;
}
@@ -343,14 +346,14 @@ static int ddebug_parse_query(char *words[], int nwords,
}
if (last)
*last++ = '\0';
if (parse_lineno(first, &query->first_lineno) < 0) {
pr_err("line-number is <0\n");
if (parse_lineno(first, &query->first_lineno) < 0)
return -EINVAL;
}
if (last) {
/* range <first>-<last> */
if (parse_lineno(last, &query->last_lineno)
< query->first_lineno) {
if (parse_lineno(last, &query->last_lineno) < 0)
return -EINVAL;
if (query->last_lineno < query->first_lineno) {
pr_err("last-line:%d < 1st-line:%d\n",
query->last_lineno,
query->first_lineno);

View File

@@ -90,8 +90,8 @@ struct flex_array *flex_array_alloc(int element_size, unsigned int total,
{
struct flex_array *ret;
int elems_per_part = 0;
int reciprocal_elems = 0;
int max_size = 0;
struct reciprocal_value reciprocal_elems = { 0 };
if (element_size) {
elems_per_part = FLEX_ARRAY_ELEMENTS_PER_PART(element_size);
@@ -119,6 +119,11 @@ EXPORT_SYMBOL(flex_array_alloc);
static int fa_element_to_part_nr(struct flex_array *fa,
unsigned int element_nr)
{
/*
* if element_size == 0 we don't get here, so we never touch
* the zeroed fa->reciprocal_elems, which would yield invalid
* results
*/
return reciprocal_divide(element_nr, fa->reciprocal_elems);
}

View File

@@ -9,7 +9,7 @@ if FONT_SUPPORT
config FONTS
bool "Select compiled-in fonts"
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
depends on FRAMEBUFFER_CONSOLE
help
Say Y here if you would like to use fonts other than the default
your frame buffer console usually use.
@@ -22,7 +22,7 @@ config FONTS
config FONT_8x8
bool "VGA 8x8 font" if FONTS
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
depends on FRAMEBUFFER_CONSOLE
default y if !SPARC && !FONTS
help
This is the "high resolution" font for the VGA frame buffer (the one
@@ -45,7 +45,7 @@ config FONT_8x16
config FONT_6x11
bool "Mac console 6x11 font (not supported by all drivers)" if FONTS
depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
depends on FRAMEBUFFER_CONSOLE
default y if !SPARC && !FONTS && MAC
help
Small console font with Macintosh-style high-half glyphs. Some Mac

View File

@@ -316,7 +316,7 @@ EXPORT_SYMBOL(gen_pool_alloc);
* gen_pool_dma_alloc - allocate special memory from the pool for DMA usage
* @pool: pool to allocate from
* @size: number of bytes to allocate from the pool
* @dma: dma-view physical address
* @dma: dma-view physical address return value. Use NULL if unneeded.
*
* Allocate the requested number of bytes from the specified pool.
* Uses the pool allocation function (with first-fit algorithm by default).
@@ -334,7 +334,8 @@ void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)
if (!vaddr)
return NULL;
*dma = gen_pool_virt_to_phys(pool, vaddr);
if (dma)
*dma = gen_pool_virt_to_phys(pool, vaddr);
return (void *)vaddr;
}

39
lib/hash.c Normal file
View File

@@ -0,0 +1,39 @@
/* General purpose hashing library
*
* That's a start of a kernel hashing library, which can be extended
* with further algorithms in future. arch_fast_hash{2,}() will
* eventually resolve to an architecture optimized implementation.
*
* Copyright 2013 Francesco Fusco <ffusco@redhat.com>
* Copyright 2013 Daniel Borkmann <dborkman@redhat.com>
* Copyright 2013 Thomas Graf <tgraf@redhat.com>
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
*/
#include <linux/jhash.h>
#include <linux/hash.h>
#include <linux/cache.h>
static struct fast_hash_ops arch_hash_ops __read_mostly = {
.hash = jhash,
.hash2 = jhash2,
};
u32 arch_fast_hash(const void *data, u32 len, u32 seed)
{
return arch_hash_ops.hash(data, len, seed);
}
EXPORT_SYMBOL_GPL(arch_fast_hash);
u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed)
{
return arch_hash_ops.hash2(data, len, seed);
}
EXPORT_SYMBOL_GPL(arch_fast_hash2);
static int __init hashlib_init(void)
{
setup_arch_fast_hash(&arch_hash_ops);
return 0;
}
early_initcall(hashlib_init);

View File

@@ -13,11 +13,11 @@
*/
#include <linux/kobject.h>
#include <linux/kobj_completion.h>
#include <linux/string.h>
#include <linux/export.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/random.h>
/**
* kobject_namespace - return @kobj's namespace tag
@@ -65,13 +65,17 @@ static int populate_dir(struct kobject *kobj)
static int create_dir(struct kobject *kobj)
{
const struct kobj_ns_type_operations *ops;
int error;
error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
if (!error) {
error = populate_dir(kobj);
if (error)
sysfs_remove_dir(kobj);
if (error)
return error;
error = populate_dir(kobj);
if (error) {
sysfs_remove_dir(kobj);
return error;
}
/*
@@ -80,7 +84,20 @@ static int create_dir(struct kobject *kobj)
*/
sysfs_get(kobj->sd);
return error;
/*
* If @kobj has ns_ops, its children need to be filtered based on
* their namespace tags. Enable namespace support on @kobj->sd.
*/
ops = kobj_child_ns_ops(kobj);
if (ops) {
BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
BUG_ON(ops->type >= KOBJ_NS_TYPES);
BUG_ON(!kobj_ns_type_registered(ops->type));
kernfs_enable_ns(kobj->sd);
}
return 0;
}
static int get_kobj_path_length(struct kobject *kobj)
@@ -247,8 +264,10 @@ int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
return 0;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
if (!kobj->name) {
kobj->name = old_name;
return -ENOMEM;
}
/* ewww... some of these buggers have '/' in the name ... */
while ((s = strchr(kobj->name, '/')))
@@ -346,7 +365,7 @@ static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
*
* If @parent is set, then the parent of the @kobj will be set to it.
* If @parent is NULL, then the parent of the @kobj will be set to the
* kobject associted with the kset assigned to this kobject. If no kset
* kobject associated with the kset assigned to this kobject. If no kset
* is assigned to the kobject, then the kobject will be located in the
* root of the sysfs tree.
*
@@ -536,7 +555,7 @@ out:
*/
void kobject_del(struct kobject *kobj)
{
struct sysfs_dirent *sd;
struct kernfs_node *sd;
if (!kobj)
return;
@@ -625,10 +644,12 @@ static void kobject_release(struct kref *kref)
{
struct kobject *kobj = container_of(kref, struct kobject, kref);
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
pr_info("kobject: '%s' (%p): %s, parent %p (delayed)\n",
kobject_name(kobj), kobj, __func__, kobj->parent);
unsigned long delay = HZ + HZ * (get_random_int() & 0x3);
pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n",
kobject_name(kobj), kobj, __func__, kobj->parent, delay);
INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
schedule_delayed_work(&kobj->release, HZ);
schedule_delayed_work(&kobj->release, delay);
#else
kobject_cleanup(kobj);
#endif
@@ -758,55 +779,7 @@ const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
/**
* kobj_completion_init - initialize a kobj_completion object.
* @kc: kobj_completion
* @ktype: type of kobject to initialize
*
* kobj_completion structures can be embedded within structures with different
* lifetime rules. During the release of the enclosing object, we can
* wait on the release of the kobject so that we don't free it while it's
* still busy.
*/
void kobj_completion_init(struct kobj_completion *kc, struct kobj_type *ktype)
{
init_completion(&kc->kc_unregister);
kobject_init(&kc->kc_kobj, ktype);
}
EXPORT_SYMBOL_GPL(kobj_completion_init);
/**
* kobj_completion_release - release a kobj_completion object
* @kobj: kobject embedded in kobj_completion
*
* Used with kobject_release to notify waiters that the kobject has been
* released.
*/
void kobj_completion_release(struct kobject *kobj)
{
struct kobj_completion *kc = kobj_to_kobj_completion(kobj);
complete(&kc->kc_unregister);
}
EXPORT_SYMBOL_GPL(kobj_completion_release);
/**
* kobj_completion_del_and_wait - release the kobject and wait for it
* @kc: kobj_completion object to release
*
* Delete the kobject from sysfs and drop the reference count. Then wait
* until any other outstanding references are also dropped. This routine
* is only necessary once other references may have been taken on the
* kobject. Typically this happens when the kobject has been published
* to sysfs via kobject_add.
*/
void kobj_completion_del_and_wait(struct kobj_completion *kc)
{
kobject_del(&kc->kc_kobj);
kobject_put(&kc->kc_kobj);
wait_for_completion(&kc->kc_unregister);
}
EXPORT_SYMBOL_GPL(kobj_completion_del_and_wait);
EXPORT_SYMBOL_GPL(kobj_sysfs_ops);
/**
* kset_register - initialize and add a kset.
@@ -835,6 +808,7 @@ void kset_unregister(struct kset *k)
{
if (!k)
return;
kobject_del(&k->kobj);
kobject_put(&k->kobj);
}

View File

@@ -88,11 +88,17 @@ out:
#ifdef CONFIG_NET
static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data)
{
struct kobject *kobj = data;
struct kobject *kobj = data, *ksobj;
const struct kobj_ns_type_operations *ops;
ops = kobj_ns_ops(kobj);
if (ops) {
if (!ops && kobj->kset) {
ksobj = &kobj->kset->kobj;
if (ksobj->parent != NULL)
ops = kobj_ns_ops(ksobj->parent);
}
if (ops && ops->netlink_ns && kobj->ktype->namespace) {
const void *sock_ns, *ns;
ns = kobj->ktype->namespace(kobj);
sock_ns = ops->netlink_ns(dsk);

View File

@@ -92,7 +92,6 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
rv = _parse_integer(s, base, &_res);
if (rv & KSTRTOX_OVERFLOW)
return -ERANGE;
rv &= ~KSTRTOX_OVERFLOW;
if (rv == 0)
return -EINVAL;
s += rv;

View File

@@ -113,6 +113,7 @@ int match_token(char *s, const match_table_t table, substring_t args[])
return p->token;
}
EXPORT_SYMBOL(match_token);
/**
* match_number: scan a number in the given base from a substring_t
@@ -163,6 +164,7 @@ int match_int(substring_t *s, int *result)
{
return match_number(s, result, 0);
}
EXPORT_SYMBOL(match_int);
/**
* match_octal: - scan an octal representation of an integer from a substring_t
@@ -177,6 +179,7 @@ int match_octal(substring_t *s, int *result)
{
return match_number(s, result, 8);
}
EXPORT_SYMBOL(match_octal);
/**
* match_hex: - scan a hex representation of an integer from a substring_t
@@ -191,6 +194,58 @@ int match_hex(substring_t *s, int *result)
{
return match_number(s, result, 16);
}
EXPORT_SYMBOL(match_hex);
/**
* match_wildcard: - parse if a string matches given wildcard pattern
* @pattern: wildcard pattern
* @str: the string to be parsed
*
* Description: Parse the string @str to check if matches wildcard
* pattern @pattern. The pattern may contain two type wildcardes:
* '*' - matches zero or more characters
* '?' - matches one character
* If it's matched, return true, else return false.
*/
bool match_wildcard(const char *pattern, const char *str)
{
const char *s = str;
const char *p = pattern;
bool star = false;
while (*s) {
switch (*p) {
case '?':
s++;
p++;
break;
case '*':
star = true;
str = s;
if (!*++p)
return true;
pattern = p;
break;
default:
if (*s == *p) {
s++;
p++;
} else {
if (!star)
return false;
str++;
s = str;
p = pattern;
}
break;
}
}
if (*p == '*')
++p;
return !*p;
}
EXPORT_SYMBOL(match_wildcard);
/**
* match_strlcpy: - Copy the characters from a substring_t to a sized buffer
@@ -213,6 +268,7 @@ size_t match_strlcpy(char *dest, const substring_t *src, size_t size)
}
return ret;
}
EXPORT_SYMBOL(match_strlcpy);
/**
* match_strdup: - allocate a new string with the contents of a substring_t
@@ -230,10 +286,4 @@ char *match_strdup(const substring_t *s)
match_strlcpy(p, s, sz);
return p;
}
EXPORT_SYMBOL(match_token);
EXPORT_SYMBOL(match_int);
EXPORT_SYMBOL(match_octal);
EXPORT_SYMBOL(match_hex);
EXPORT_SYMBOL(match_strlcpy);
EXPORT_SYMBOL(match_strdup);

View File

@@ -120,6 +120,9 @@ static void percpu_ref_kill_rcu(struct rcu_head *rcu)
atomic_add((int) count - PCPU_COUNT_BIAS, &ref->count);
WARN_ONCE(atomic_read(&ref->count) <= 0, "percpu ref <= 0 (%i)",
atomic_read(&ref->count));
/* @ref is viewed as dead on all CPUs, send out kill confirmation */
if (ref->confirm_kill)
ref->confirm_kill(ref);

View File

@@ -132,22 +132,22 @@ static inline unsigned alloc_local_tag(struct percpu_ida_cpu *tags)
/**
* percpu_ida_alloc - allocate a tag
* @pool: pool to allocate from
* @gfp: gfp flags
* @state: task state for prepare_to_wait
*
* Returns a tag - an integer in the range [0..nr_tags) (passed to
* tag_pool_init()), or otherwise -ENOSPC on allocation failure.
*
* Safe to be called from interrupt context (assuming it isn't passed
* __GFP_WAIT, of course).
* TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, of course).
*
* @gfp indicates whether or not to wait until a free id is available (it's not
* used for internal memory allocations); thus if passed __GFP_WAIT we may sleep
* however long it takes until another thread frees an id (same semantics as a
* mempool).
*
* Will not fail if passed __GFP_WAIT.
* Will not fail if passed TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE.
*/
int percpu_ida_alloc(struct percpu_ida *pool, gfp_t gfp)
int percpu_ida_alloc(struct percpu_ida *pool, int state)
{
DEFINE_WAIT(wait);
struct percpu_ida_cpu *tags;
@@ -174,7 +174,8 @@ int percpu_ida_alloc(struct percpu_ida *pool, gfp_t gfp)
*
* global lock held and irqs disabled, don't need percpu lock
*/
prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
if (state != TASK_RUNNING)
prepare_to_wait(&pool->wait, &wait, state);
if (!tags->nr_free)
alloc_global_tags(pool, tags);
@@ -191,16 +192,22 @@ int percpu_ida_alloc(struct percpu_ida *pool, gfp_t gfp)
spin_unlock(&pool->lock);
local_irq_restore(flags);
if (tag >= 0 || !(gfp & __GFP_WAIT))
if (tag >= 0 || state == TASK_RUNNING)
break;
if (signal_pending_state(state, current)) {
tag = -ERESTARTSYS;
break;
}
schedule();
local_irq_save(flags);
tags = this_cpu_ptr(pool->tag_cpu);
}
if (state != TASK_RUNNING)
finish_wait(&pool->wait, &wait);
finish_wait(&pool->wait, &wait);
return tag;
}
EXPORT_SYMBOL_GPL(percpu_ida_alloc);

View File

@@ -8,8 +8,8 @@
#define CHECK_LOOPS 100
struct test_node {
struct rb_node rb;
u32 key;
struct rb_node rb;
/* following fields used for testing augmented rbtree functionality */
u32 val;
@@ -114,6 +114,16 @@ static int black_path_count(struct rb_node *rb)
return count;
}
static void check_postorder_foreach(int nr_nodes)
{
struct test_node *cur, *n;
int count = 0;
rbtree_postorder_for_each_entry_safe(cur, n, &root, rb)
count++;
WARN_ON_ONCE(count != nr_nodes);
}
static void check_postorder(int nr_nodes)
{
struct rb_node *rb;
@@ -148,6 +158,7 @@ static void check(int nr_nodes)
WARN_ON_ONCE(count < (1 << black_path_count(rb_last(&root))) - 1);
check_postorder(nr_nodes);
check_postorder_foreach(nr_nodes);
}
static void check_augmented(int nr_nodes)

View File

@@ -1,11 +1,27 @@
#include <linux/kernel.h>
#include <asm/div64.h>
#include <linux/reciprocal_div.h>
#include <linux/export.h>
u32 reciprocal_value(u32 k)
/*
* For a description of the algorithm please have a look at
* include/linux/reciprocal_div.h
*/
struct reciprocal_value reciprocal_value(u32 d)
{
u64 val = (1LL << 32) + (k - 1);
do_div(val, k);
return (u32)val;
struct reciprocal_value R;
u64 m;
int l;
l = fls(d - 1);
m = ((1ULL << 32) * ((1ULL << l) - d));
do_div(m, d);
++m;
R.m = (u32)m;
R.sh1 = min(l, 1);
R.sh2 = max(l - 1, 0);
return R;
}
EXPORT_SYMBOL(reciprocal_value);

View File

@@ -495,7 +495,7 @@ static bool sg_miter_get_next_page(struct sg_mapping_iter *miter)
* true if @miter contains the valid mapping. false if end of sg
* list is reached.
*/
static bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
{
sg_miter_stop(miter);
@@ -513,6 +513,7 @@ static bool sg_miter_skip(struct sg_mapping_iter *miter, off_t offset)
return true;
}
EXPORT_SYMBOL(sg_miter_skip);
/**
* sg_miter_next - proceed mapping iterator to the next mapping

View File

@@ -17,9 +17,6 @@ void show_mem(unsigned int filter)
printk("Mem-Info:\n");
show_free_areas(filter);
if (filter & SHOW_MEM_FILTER_PAGE_COUNT)
return;
for_each_online_pgdat(pgdat) {
unsigned long flags;
int zoneid;
@@ -46,4 +43,7 @@ void show_mem(unsigned int filter)
printk("%lu pages in pagetable cache\n",
quicklist_total_size());
#endif
#ifdef CONFIG_MEMORY_FAILURE
printk("%lu pages hwpoisoned\n", atomic_long_read(&num_poisoned_pages));
#endif
}

View File

@@ -172,8 +172,9 @@ int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
/*
* Get the overflow emergency buffer
*/
v_overflow_buffer = alloc_bootmem_low_pages_nopanic(
PAGE_ALIGN(io_tlb_overflow));
v_overflow_buffer = memblock_virt_alloc_low_nopanic(
PAGE_ALIGN(io_tlb_overflow),
PAGE_SIZE);
if (!v_overflow_buffer)
return -ENOMEM;
@@ -184,11 +185,15 @@ int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
* to find contiguous free memory regions of size up to IO_TLB_SEGSIZE
* between io_tlb_start and io_tlb_end.
*/
io_tlb_list = alloc_bootmem_pages(PAGE_ALIGN(io_tlb_nslabs * sizeof(int)));
io_tlb_list = memblock_virt_alloc(
PAGE_ALIGN(io_tlb_nslabs * sizeof(int)),
PAGE_SIZE);
for (i = 0; i < io_tlb_nslabs; i++)
io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE);
io_tlb_index = 0;
io_tlb_orig_addr = alloc_bootmem_pages(PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)));
io_tlb_orig_addr = memblock_virt_alloc(
PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)),
PAGE_SIZE);
if (verbose)
swiotlb_print_info();
@@ -215,13 +220,13 @@ swiotlb_init(int verbose)
bytes = io_tlb_nslabs << IO_TLB_SHIFT;
/* Get IO TLB memory from the low pages */
vstart = alloc_bootmem_low_pages_nopanic(PAGE_ALIGN(bytes));
vstart = memblock_virt_alloc_low_nopanic(PAGE_ALIGN(bytes), PAGE_SIZE);
if (vstart && !swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose))
return;
if (io_tlb_start)
free_bootmem(io_tlb_start,
PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT));
memblock_free_early(io_tlb_start,
PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT));
pr_warn("Cannot allocate SWIOTLB buffer");
no_iotlb_memory = true;
}
@@ -357,14 +362,14 @@ void __init swiotlb_free(void)
free_pages((unsigned long)phys_to_virt(io_tlb_start),
get_order(io_tlb_nslabs << IO_TLB_SHIFT));
} else {
free_bootmem_late(io_tlb_overflow_buffer,
PAGE_ALIGN(io_tlb_overflow));
free_bootmem_late(__pa(io_tlb_orig_addr),
PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)));
free_bootmem_late(__pa(io_tlb_list),
PAGE_ALIGN(io_tlb_nslabs * sizeof(int)));
free_bootmem_late(io_tlb_start,
PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT));
memblock_free_late(io_tlb_overflow_buffer,
PAGE_ALIGN(io_tlb_overflow));
memblock_free_late(__pa(io_tlb_orig_addr),
PAGE_ALIGN(io_tlb_nslabs * sizeof(phys_addr_t)));
memblock_free_late(__pa(io_tlb_list),
PAGE_ALIGN(io_tlb_nslabs * sizeof(int)));
memblock_free_late(io_tlb_start,
PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT));
}
io_tlb_nslabs = 0;
}
@@ -505,7 +510,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
not_found:
spin_unlock_irqrestore(&io_tlb_lock, flags);
dev_warn(hwdev, "swiotlb buffer is full\n");
if (printk_ratelimit())
dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes)\n", size);
return SWIOTLB_MAP_ERROR;
found:
spin_unlock_irqrestore(&io_tlb_lock, flags);

33
lib/test_module.c Normal file
View File

@@ -0,0 +1,33 @@
/*
* This module emits "Hello, world" on printk when loaded.
*
* It is designed to be used for basic evaluation of the module loading
* subsystem (for example when validating module signing/verification). It
* lacks any extra dependencies, and will not normally be loaded by the
* system unless explicitly requested by name.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
static int __init test_module_init(void)
{
pr_warn("Hello, world\n");
return 0;
}
module_init(test_module_init);
static void __exit test_module_exit(void)
{
pr_warn("Goodbye\n");
}
module_exit(test_module_exit);
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
MODULE_LICENSE("GPL");

110
lib/test_user_copy.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Kernel module for testing copy_to/from_user infrastructure.
*
* Copyright 2013 Google Inc. All Rights Reserved
*
* Authors:
* Kees Cook <keescook@chromium.org>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/mman.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#define test(condition, msg) \
({ \
int cond = (condition); \
if (cond) \
pr_warn("%s\n", msg); \
cond; \
})
static int __init test_user_copy_init(void)
{
int ret = 0;
char *kmem;
char __user *usermem;
char *bad_usermem;
unsigned long user_addr;
unsigned long value = 0x5A;
kmem = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!kmem)
return -ENOMEM;
user_addr = vm_mmap(NULL, 0, PAGE_SIZE * 2,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANONYMOUS | MAP_PRIVATE, 0);
if (user_addr >= (unsigned long)(TASK_SIZE)) {
pr_warn("Failed to allocate user memory\n");
kfree(kmem);
return -ENOMEM;
}
usermem = (char __user *)user_addr;
bad_usermem = (char *)user_addr;
/* Legitimate usage: none of these should fail. */
ret |= test(copy_from_user(kmem, usermem, PAGE_SIZE),
"legitimate copy_from_user failed");
ret |= test(copy_to_user(usermem, kmem, PAGE_SIZE),
"legitimate copy_to_user failed");
ret |= test(get_user(value, (unsigned long __user *)usermem),
"legitimate get_user failed");
ret |= test(put_user(value, (unsigned long __user *)usermem),
"legitimate put_user failed");
/* Invalid usage: none of these should succeed. */
ret |= test(!copy_from_user(kmem, (char __user *)(kmem + PAGE_SIZE),
PAGE_SIZE),
"illegal all-kernel copy_from_user passed");
ret |= test(!copy_from_user(bad_usermem, (char __user *)kmem,
PAGE_SIZE),
"illegal reversed copy_from_user passed");
ret |= test(!copy_to_user((char __user *)kmem, kmem + PAGE_SIZE,
PAGE_SIZE),
"illegal all-kernel copy_to_user passed");
ret |= test(!copy_to_user((char __user *)kmem, bad_usermem,
PAGE_SIZE),
"illegal reversed copy_to_user passed");
ret |= test(!get_user(value, (unsigned long __user *)kmem),
"illegal get_user passed");
ret |= test(!put_user(value, (unsigned long __user *)kmem),
"illegal put_user passed");
vm_munmap(user_addr, PAGE_SIZE * 2);
kfree(kmem);
if (ret == 0) {
pr_info("tests passed.\n");
return 0;
}
return -EINVAL;
}
module_init(test_user_copy_init);
static void __exit test_user_copy_exit(void)
{
pr_info("unloaded.\n");
}
module_exit(test_user_copy_exit);
MODULE_AUTHOR("Kees Cook <keescook@chromium.org>");
MODULE_LICENSE("GPL");

View File

@@ -1155,6 +1155,30 @@ char *netdev_feature_string(char *buf, char *end, const u8 *addr,
return number(buf, end, *(const netdev_features_t *)addr, spec);
}
static noinline_for_stack
char *address_val(char *buf, char *end, const void *addr,
struct printf_spec spec, const char *fmt)
{
unsigned long long num;
spec.flags |= SPECIAL | SMALL | ZEROPAD;
spec.base = 16;
switch (fmt[1]) {
case 'd':
num = *(const dma_addr_t *)addr;
spec.field_width = sizeof(dma_addr_t) * 2 + 2;
break;
case 'p':
default:
num = *(const phys_addr_t *)addr;
spec.field_width = sizeof(phys_addr_t) * 2 + 2;
break;
}
return number(buf, end, num, spec);
}
int kptr_restrict __read_mostly;
/*
@@ -1218,7 +1242,8 @@ int kptr_restrict __read_mostly;
* N no separator
* The maximum supported length is 64 bytes of the input. Consider
* to use print_hex_dump() for the larger input.
* - 'a' For a phys_addr_t type and its derivative types (passed by reference)
* - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives
* (default assumed to be phys_addr_t, passed by reference)
* - 'd[234]' For a dentry name (optionally 2-4 last components)
* - 'D[234]' Same as 'd' but for a struct file
*
@@ -1353,11 +1378,7 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr,
}
break;
case 'a':
spec.flags |= SPECIAL | SMALL | ZEROPAD;
spec.field_width = sizeof(phys_addr_t) * 2 + 2;
spec.base = 16;
return number(buf, end,
(unsigned long long) *((phys_addr_t *)ptr), spec);
return address_val(buf, end, ptr, spec, fmt);
case 'd':
return dentry_name(buf, end, ptr, spec, fmt);
case 'D':