From 87e6f968339bcdda56b39572c7e63331192296a0 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:11:13 -0500 Subject: dynamic_debug: drop enabled field from struct _ddebug, use _DPRINTK_FLAGS_PRINT Currently any enabled dynamic-debug flag on a pr_debug callsite will enable printing, even if _DPRINTK_FLAGS_PRINT is off. Checking print flag directly allows "-p" to disable callsites without fussing with other flags, so the following disables everything, without altering flags user may have set: echo -p > $DBGFS/dynamic_debug/control Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 0564e3c39882..f71a6b046245 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -28,7 +28,6 @@ struct _ddebug { #define _DPRINTK_FLAGS_INCL_TID (1<<4) #define _DPRINTK_FLAGS_DEFAULT 0 unsigned int flags:8; - char enabled; } __attribute__((aligned(8))); @@ -62,21 +61,20 @@ int __dynamic_netdev_dbg(struct _ddebug *descriptor, .format = (fmt), \ .lineno = __LINE__, \ .flags = _DPRINTK_FLAGS_DEFAULT, \ - .enabled = false, \ } #define dynamic_pr_debug(fmt, ...) \ do { \ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \ ##__VA_ARGS__); \ } while (0) #define dynamic_dev_dbg(dev, fmt, ...) \ do { \ - DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_dev_dbg(&descriptor, dev, fmt, \ ##__VA_ARGS__); \ } while (0) @@ -84,7 +82,7 @@ do { \ #define dynamic_netdev_dbg(dev, fmt, ...) \ do { \ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ - if (unlikely(descriptor.enabled)) \ + if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ __dynamic_netdev_dbg(&descriptor, dev, fmt, \ ##__VA_ARGS__); \ } while (0) -- cgit v1.2.3 From b558c96ffa53f4b3dd52b774e4fb7a52982ab52b Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:11:18 -0500 Subject: dynamic_debug: make dynamic-debug supersede DEBUG ccflag If CONFIG_DYNAMIC_DEBUG is defined, honor it over DEBUG, so that pr_debug()s are controllable, instead of always-on. When DEBUG is also defined, change _DPRINTK_FLAGS_DEFAULT to enable printing by default. Also adding _DPRINTK_FLAGS_INCL_MODNAME would be nice, but there are numerous cases of pr_debug(NAME ": ...), which would result in double printing of module-name. So defer this until things settle. Cc: David Miller Cc: Joe Perches Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 8 ++++---- include/linux/dynamic_debug.h | 4 ++++ include/linux/netdevice.h | 8 ++++---- include/linux/printk.h | 8 ++++---- 4 files changed, 16 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 5b3adb8f9588..a782d7ff9e8b 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -945,14 +945,14 @@ int _dev_info(const struct device *dev, const char *fmt, ...) #define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg) -#if defined(DEBUG) -#define dev_dbg(dev, format, arg...) \ - dev_printk(KERN_DEBUG, dev, format, ##arg) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) #define dev_dbg(dev, format, ...) \ do { \ dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \ } while (0) +#elif defined(DEBUG) +#define dev_dbg(dev, format, arg...) \ + dev_printk(KERN_DEBUG, dev, format, ##arg) #else #define dev_dbg(dev, format, arg...) \ ({ \ diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index f71a6b046245..29ea09ae30cf 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -26,7 +26,11 @@ struct _ddebug { #define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2) #define _DPRINTK_FLAGS_INCL_LINENO (1<<3) #define _DPRINTK_FLAGS_INCL_TID (1<<4) +#if defined DEBUG +#define _DPRINTK_FLAGS_DEFAULT _DPRINTK_FLAGS_PRINT +#else #define _DPRINTK_FLAGS_DEFAULT 0 +#endif unsigned int flags:8; } __attribute__((aligned(8))); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0eac07c95255..f486f635e7b5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2687,14 +2687,14 @@ int netdev_info(const struct net_device *dev, const char *format, ...); #define MODULE_ALIAS_NETDEV(device) \ MODULE_ALIAS("netdev-" device) -#if defined(DEBUG) -#define netdev_dbg(__dev, format, args...) \ - netdev_printk(KERN_DEBUG, __dev, format, ##args) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) #define netdev_dbg(__dev, format, args...) \ do { \ dynamic_netdev_dbg(__dev, format, ##args); \ } while (0) +#elif defined(DEBUG) +#define netdev_dbg(__dev, format, args...) \ + netdev_printk(KERN_DEBUG, __dev, format, ##args) #else #define netdev_dbg(__dev, format, args...) \ ({ \ diff --git a/include/linux/printk.h b/include/linux/printk.h index f0e22f75143f..f9abd9357a0c 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -180,13 +180,13 @@ extern void dump_stack(void) __cold; #endif /* If you are writing a driver, please use dev_dbg instead */ -#if defined(DEBUG) -#define pr_debug(fmt, ...) \ - printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) -#elif defined(CONFIG_DYNAMIC_DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) /* dynamic_pr_debug() uses pr_fmt() internally so we don't need it here */ #define pr_debug(fmt, ...) \ dynamic_pr_debug(fmt, ##__VA_ARGS__) +#elif defined(DEBUG) +#define pr_debug(fmt, ...) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) -- cgit v1.2.3 From 5ca7d2a6c5e4f24dfe39e8383c6d32e61d95d16a Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:44 -0500 Subject: dynamic_debug: describe_flags with '=[pmflt_]*' Change describe_flags() to emit '=[pmflt_]+' for current callsite flags, or just '=_' when they're disabled. Having '=' in output allows a more selective grep expression; in contrast '-' may appear in filenames, line-ranges, and format-strings. '=' also has better mnemonics, saying; "the current setting is equal to ". This allows grep "=_" /dynamic_debug/control to see disabled callsites while avoiding the many occurrences of " = " seen in format strings. Enlarge flagsbufs to handle additional flag char, and alter ddebug_parse_flags() to allow flags=0, so that user can turn off all debug flags via: ~# echo =_ > /dynamic_debug/control Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 3 ++- lib/dynamic_debug.c | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 29ea09ae30cf..fc39640f2dea 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -21,7 +21,8 @@ struct _ddebug { * The bits here are changed dynamically when the user * writes commands to /dynamic_debug/control */ -#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ +#define _DPRINTK_FLAGS_NONE 0 +#define _DPRINTK_FLAGS_PRINT (1<<0) /* printk() a message using the format */ #define _DPRINTK_FLAGS_INCL_MODNAME (1<<1) #define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2) #define _DPRINTK_FLAGS_INCL_LINENO (1<<3) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index b199e0935053..cde4dfe2b2d5 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -75,6 +75,7 @@ static struct { unsigned flag:8; char opt_char; } opt_array[] = { { _DPRINTK_FLAGS_INCL_FUNCNAME, 'f' }, { _DPRINTK_FLAGS_INCL_LINENO, 'l' }, { _DPRINTK_FLAGS_INCL_TID, 't' }, + { _DPRINTK_FLAGS_NONE, '_' }, }; /* format a string into buf[] which describes the _ddebug's flags */ @@ -84,12 +85,12 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, char *p = buf; int i; - BUG_ON(maxlen < 4); + BUG_ON(maxlen < 6); for (i = 0; i < ARRAY_SIZE(opt_array); ++i) if (dp->flags & opt_array[i].flag) *p++ = opt_array[i].opt_char; if (p == buf) - *p++ = '-'; + *p++ = '_'; *p = '\0'; return buf; @@ -108,7 +109,7 @@ static void ddebug_change(const struct ddebug_query *query, struct ddebug_table *dt; unsigned int newflags; unsigned int nfound = 0; - char flagbuf[8]; + char flagbuf[10]; /* search for matching ddebugs */ mutex_lock(&ddebug_lock); @@ -152,7 +153,7 @@ static void ddebug_change(const struct ddebug_query *query, continue; dp->flags = newflags; if (verbose) - pr_info("changed %s:%d [%s]%s %s\n", + pr_info("changed %s:%d [%s]%s =%s\n", dp->filename, dp->lineno, dt->mod_name, dp->function, ddebug_describe_flags(dp, flagbuf, @@ -370,8 +371,6 @@ static int ddebug_parse_flags(const char *str, unsigned int *flagsp, if (i < 0) return -EINVAL; } - if (flags == 0) - return -EINVAL; if (verbose) pr_info("flags=0x%x\n", flags); @@ -666,7 +665,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p) { struct ddebug_iter *iter = m->private; struct _ddebug *dp = p; - char flagsbuf[8]; + char flagsbuf[10]; if (verbose) pr_info("called m=%p p=%p\n", m, p); @@ -677,10 +676,10 @@ static int ddebug_proc_show(struct seq_file *m, void *p) return 0; } - seq_printf(m, "%s:%u [%s]%s %s \"", - dp->filename, dp->lineno, - iter->table->mod_name, dp->function, - ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); + seq_printf(m, "%s:%u [%s]%s =%s \"", + dp->filename, dp->lineno, + iter->table->mod_name, dp->function, + ddebug_describe_flags(dp, flagsbuf, sizeof(flagsbuf))); seq_escape(m, dp->format, "\t\r\n\""); seq_puts(m, "\"\n"); -- cgit v1.2.3 From e703ddae383abb24b1c7f363cb0df7e78a44ea45 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:59 -0500 Subject: dynamic_debug: reduce lineno field to a saner 18 bits lineno:24 allows files with 4 million lines, an insane file-size, even for never-to-get-in-tree machine generated code. Reduce this to 18 bits, which still allows 256k lines. This is still insanely big, but its not raving mad. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- include/linux/dynamic_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index fc39640f2dea..7e3c53a900d8 100644 --- a/include/linux/dynamic_debug.h +++ b/include/linux/dynamic_debug.h @@ -15,7 +15,7 @@ struct _ddebug { const char *function; const char *filename; const char *format; - unsigned int lineno:24; + unsigned int lineno:18; /* * The flags field controls the behaviour at the callsite. * The bits here are changed dynamically when the user -- cgit v1.2.3 From 9875bb480cc89d9b690f7028aadf7e58454f0dae Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 13:35:37 -0500 Subject: Eliminate get_driver() and put_driver() Now that there are no users of get_driver() or put_driver(), this patch (as1513) removes those routines completely. Signed-off-by: Alan Stern CC: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/base/driver.c | 28 ---------------------------- include/linux/device.h | 2 -- 2 files changed, 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index e979cad75c6e..60e4f77ca662 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -153,34 +153,6 @@ int driver_add_kobj(struct device_driver *drv, struct kobject *kobj, } EXPORT_SYMBOL_GPL(driver_add_kobj); -/** - * get_driver - increment driver reference count. - * @drv: driver. - */ -struct device_driver *get_driver(struct device_driver *drv) -{ - if (drv) { - struct driver_private *priv; - struct kobject *kobj; - - kobj = kobject_get(&drv->p->kobj); - priv = to_driver(kobj); - return priv->driver; - } - return NULL; -} -EXPORT_SYMBOL_GPL(get_driver); - -/** - * put_driver - decrement driver's refcount. - * @drv: driver. - */ -void put_driver(struct device_driver *drv) -{ - kobject_put(&drv->p->kobj); -} -EXPORT_SYMBOL_GPL(put_driver); - static int driver_add_groups(struct device_driver *drv, const struct attribute_group **groups) { diff --git a/include/linux/device.h b/include/linux/device.h index a782d7ff9e8b..d28bd8295677 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -238,8 +238,6 @@ struct device_driver { extern int __must_check driver_register(struct device_driver *drv); extern void driver_unregister(struct device_driver *drv); -extern struct device_driver *get_driver(struct device_driver *drv); -extern void put_driver(struct device_driver *drv); extern struct device_driver *driver_find(const char *name, struct bus_type *bus); extern int driver_probe_done(void); -- cgit v1.2.3 From 644e9cbbe3fc032cc92d0936057e166a994dc246 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:05 +0100 Subject: Add driver auto probing for x86 features v4 There's a growing number of drivers that support a specific x86 feature or CPU. Currently loading these drivers currently on a generic distribution requires various driver specific hacks and it often doesn't work. This patch adds auto probing for drivers based on the x86 cpuid information, in particular based on vendor/family/model number and also based on CPUID feature bits. For example a common issue is not loading the SSE 4.2 accelerated CRC module: this can significantly lower the performance of BTRFS which relies on fast CRC. Another issue is loading the right CPUFREQ driver for the current CPU. Currently distributions often try all all possible driver until one sticks, which is not really a good way to do this. It works with existing udev without any changes. The code exports the x86 information as a generic string in sysfs that can be matched by udev's pattern matching. This scheme does not support numeric ranges, so if you want to handle e.g. ranges of model numbers they have to be encoded in ASCII or simply all models or families listed. Fixing that would require changing udev. Another issue is that udev will happily load all drivers that match, there is currently no nice way to stop a specific driver from being loaded if it's not needed (e.g. if you don't need fast CRC) But there are not that many cpu specific drivers around and they're all not that bloated, so this isn't a particularly serious issue. Originally this patch added the modalias to the normal cpu sysdevs. However sysdevs don't have all the infrastructure needed for udev, so it couldn't really autoload drivers. This patch instead adds the CPU modaliases to the cpuid devices, which are real devices with full support for udev. This implies that the cpuid driver has to be loaded to use this. This patch just adds infrastructure, some driver conversions in followups. Thanks to Kay for helping with some sysfs magic. v2: Constifcation, some updates v4: (trenn@suse.de): - Use kzalloc instead of kmalloc to terminate modalias buffer - Use uppercase hex values to match correctly against hex values containing letters Cc: Dave Jones Cc: Kay Sievers Cc: Jen Axboe Cc: Herbert Xu Cc: Huang Ying Cc: Len Brown Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/cpu_device_id.h | 13 ++++++++ arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/cpu/match.c | 48 +++++++++++++++++++++++++++++ arch/x86/kernel/cpuid.c | 59 +++++++++++++++++++++++++++++++++++- include/linux/mod_devicetable.h | 21 +++++++++++++ scripts/mod/file2alias.c | 24 +++++++++++++++ 6 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/cpu_device_id.h create mode 100644 arch/x86/kernel/cpu/match.c (limited to 'include/linux') diff --git a/arch/x86/include/asm/cpu_device_id.h b/arch/x86/include/asm/cpu_device_id.h new file mode 100644 index 000000000000..ff501e511d91 --- /dev/null +++ b/arch/x86/include/asm/cpu_device_id.h @@ -0,0 +1,13 @@ +#ifndef _CPU_DEVICE_ID +#define _CPU_DEVICE_ID 1 + +/* + * Declare drivers belonging to specific x86 CPUs + * Similar in spirit to pci_device_id and related PCI functions + */ + +#include + +extern const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match); + +#endif diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 25f24dccdcfa..6ab6aa2fdfdd 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -16,6 +16,7 @@ obj-y := intel_cacheinfo.o scattered.o topology.o obj-y += proc.o capflags.o powerflags.o common.o obj-y += vmware.o hypervisor.o sched.o mshyperv.o obj-y += rdrand.o +obj-y += match.o obj-$(CONFIG_X86_32) += bugs.o obj-$(CONFIG_X86_64) += bugs_64.o diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c new file mode 100644 index 000000000000..7acc961422e7 --- /dev/null +++ b/arch/x86/kernel/cpu/match.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +/** + * x86_match_cpu - match current CPU again an array of x86_cpu_ids + * @match: Pointer to array of x86_cpu_ids. Last entry terminated with + * {}. + * + * Return the entry if the current CPU matches the entries in the + * passed x86_cpu_id match table. Otherwise NULL. The match table + * contains vendor (X86_VENDOR_*), family, model and feature bits or + * respective wildcard entries. + * + * A typical table entry would be to match a specific CPU + * { X86_VENDOR_INTEL, 6, 0x12 } + * or to match a specific CPU feature + * { X86_FEATURE_MATCH(X86_FEATURE_FOOBAR) } + * + * Fields can be wildcarded with %X86_VENDOR_ANY, %X86_FAMILY_ANY, + * %X86_MODEL_ANY, %X86_FEATURE_ANY or 0 (except for vendor) + * + * Arrays used to match for this should also be declared using + * MODULE_DEVICE_TABLE(x86_cpu, ...) + * + * This always matches against the boot cpu, assuming models and features are + * consistent over all CPUs. + */ +const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) +{ + const struct x86_cpu_id *m; + struct cpuinfo_x86 *c = &boot_cpu_data; + + for (m = match; m->vendor | m->family | m->model | m->feature; m++) { + if (m->vendor != X86_VENDOR_ANY && c->x86_vendor != m->vendor) + continue; + if (m->family != X86_FAMILY_ANY && c->x86 != m->family) + continue; + if (m->model != X86_MODEL_ANY && c->x86_model != m->model) + continue; + if (m->feature != X86_FEATURE_ANY && !cpu_has(c, m->feature)) + continue; + return m; + } + return NULL; +} +EXPORT_SYMBOL(x86_match_cpu); diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index a524353d93f2..7c89880eefd0 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -138,13 +139,57 @@ static const struct file_operations cpuid_fops = { .open = cpuid_open, }; +static ssize_t print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr) +{ + int size = PAGE_SIZE; + int i, n; + char *buf = bufptr; + + n = snprintf(buf, size, "x86cpu:vendor:%04X:family:" + "%04X:model:%04X:feature:", + boot_cpu_data.x86_vendor, + boot_cpu_data.x86, + boot_cpu_data.x86_model); + size -= n; + buf += n; + size -= 2; + for (i = 0; i < NCAPINTS*32; i++) { + if (boot_cpu_has(i)) { + n = snprintf(buf, size, ",%04X", i); + if (n < 0) { + WARN(1, "x86 features overflow page\n"); + break; + } + size -= n; + buf += n; + } + } + *buf++ = ','; + *buf++ = '\n'; + return buf - bufptr; +} + +static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); + static __cpuinit int cpuid_device_create(int cpu) { struct device *dev; + int err; dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); - return IS_ERR(dev) ? PTR_ERR(dev) : 0; + if (IS_ERR(dev)) + return PTR_ERR(dev); + + err = device_create_file(dev, &dev_attr_modalias); + if (err) { + /* keep device around on error. attribute is optional. */ + err = 0; + } + + return 0; } static void cpuid_device_destroy(int cpu) @@ -182,6 +227,17 @@ static char *cpuid_devnode(struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } +static int cpuid_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf) { + print_cpu_modalias(NULL, NULL, buf); + add_uevent_var(env, "MODALIAS=%s", buf); + kfree(buf); + } + return 0; +} + static int __init cpuid_init(void) { int i, err = 0; @@ -200,6 +256,7 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; + cpuid_class->dev_uevent = cpuid_dev_uevent; for_each_online_cpu(i) { err = cpuid_device_create(i); if (err != 0) diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b29e7f6f8fa5..cff2cc08f45a 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -571,4 +571,25 @@ struct amba_id { #endif }; +/* + * Match x86 CPUs for CPU specific drivers. + * See documentation of "x86_match_cpu" for details. + */ + +struct x86_cpu_id { + __u16 vendor; + __u16 family; + __u16 model; + __u16 feature; /* bit index */ + kernel_ulong_t driver_data; +}; + +#define X86_FEATURE_MATCH(x) \ + { X86_VENDOR_ANY, X86_FAMILY_ANY, X86_MODEL_ANY, x } + +#define X86_VENDOR_ANY 0xffff +#define X86_FAMILY_ANY 0 +#define X86_MODEL_ANY 0 +#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */ + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index c0e14b3f2306..026ba38759ca 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1013,6 +1013,30 @@ static int do_amba_entry(const char *filename, } ADD_TO_DEVTABLE("amba", struct amba_id, do_amba_entry); +/* LOOKS like x86cpu:vendor:VVVV:family:FFFF:model:MMMM:feature:*,FEAT,* + * All fields are numbers. It would be nicer to use strings for vendor + * and feature, but getting those out of the build system here is too + * complicated. + */ + +static int do_x86cpu_entry(const char *filename, struct x86_cpu_id *id, + char *alias) +{ + id->feature = TO_NATIVE(id->feature); + id->family = TO_NATIVE(id->family); + id->model = TO_NATIVE(id->model); + id->vendor = TO_NATIVE(id->vendor); + + strcpy(alias, "x86cpu:"); + ADD(alias, "vendor:", id->vendor != X86_VENDOR_ANY, id->vendor); + ADD(alias, ":family:", id->family != X86_FAMILY_ANY, id->family); + ADD(alias, ":model:", id->model != X86_MODEL_ANY, id->model); + ADD(alias, ":feature:*,", id->feature != X86_FEATURE_ANY, id->feature); + strcat(alias, ",*"); + return 1; +} +ADD_TO_DEVTABLE("x86cpu", struct x86_cpu_id, do_x86cpu_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3 From fad12ac8c8c2591c7f4e61d19b6a9d76cd49fafa Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 26 Jan 2012 00:09:14 +0100 Subject: CPU: Introduce ARCH_HAS_CPU_AUTOPROBE and X86 parts This patch is based on Andi Kleen's work: Implement autoprobing/loading of modules serving CPU specific features (x86cpu autoloading). And Kay Siever's work to get rid of sysdev cpu structures and making use of struct device instead. Before, the cpuid driver had to be loaded to get the x86cpu autoloading feature. With this patch autoloading works through the /sys/devices/system/cpu object Cc: Kay Sievers Cc: Dave Jones Cc: Jens Axboe Cc: Herbert Xu Cc: Huang Ying Cc: Len Brown Acked-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/Kconfig | 3 +++ arch/x86/kernel/cpu/match.c | 44 +++++++++++++++++++++++++++++++++ arch/x86/kernel/cpuid.c | 59 +-------------------------------------------- drivers/base/cpu.c | 11 +++++++++ include/linux/cpu.h | 7 ++++++ 5 files changed, 66 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 864cc6e6ac8e..6baa1e66e1bc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -179,6 +179,9 @@ config ARCH_HAS_DEFAULT_IDLE config ARCH_HAS_CACHE_LINE_SIZE def_bool y +config ARCH_HAS_CPU_AUTOPROBE + def_bool y + config HAVE_SETUP_PER_CPU_AREA def_bool y diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c index 7acc961422e7..940e2d483076 100644 --- a/arch/x86/kernel/cpu/match.c +++ b/arch/x86/kernel/cpu/match.c @@ -2,6 +2,7 @@ #include #include #include +#include /** * x86_match_cpu - match current CPU again an array of x86_cpu_ids @@ -46,3 +47,46 @@ const struct x86_cpu_id *x86_match_cpu(const struct x86_cpu_id *match) return NULL; } EXPORT_SYMBOL(x86_match_cpu); + +ssize_t arch_print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr) +{ + int size = PAGE_SIZE; + int i, n; + char *buf = bufptr; + + n = snprintf(buf, size, "x86cpu:vendor:%04X:family:%04X:" + "model:%04X:feature:", + boot_cpu_data.x86_vendor, + boot_cpu_data.x86, + boot_cpu_data.x86_model); + size -= n; + buf += n; + size -= 2; + for (i = 0; i < NCAPINTS*32; i++) { + if (boot_cpu_has(i)) { + n = snprintf(buf, size, ",%04X", i); + if (n < 0) { + WARN(1, "x86 features overflow page\n"); + break; + } + size -= n; + buf += n; + } + } + *buf++ = ','; + *buf++ = '\n'; + return buf - bufptr; +} + +int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf) { + arch_print_cpu_modalias(NULL, NULL, buf); + add_uevent_var(env, "MODALIAS=%s", buf); + kfree(buf); + } + return 0; +} diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index 7c89880eefd0..a524353d93f2 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include @@ -139,57 +138,13 @@ static const struct file_operations cpuid_fops = { .open = cpuid_open, }; -static ssize_t print_cpu_modalias(struct device *dev, - struct device_attribute *attr, - char *bufptr) -{ - int size = PAGE_SIZE; - int i, n; - char *buf = bufptr; - - n = snprintf(buf, size, "x86cpu:vendor:%04X:family:" - "%04X:model:%04X:feature:", - boot_cpu_data.x86_vendor, - boot_cpu_data.x86, - boot_cpu_data.x86_model); - size -= n; - buf += n; - size -= 2; - for (i = 0; i < NCAPINTS*32; i++) { - if (boot_cpu_has(i)) { - n = snprintf(buf, size, ",%04X", i); - if (n < 0) { - WARN(1, "x86 features overflow page\n"); - break; - } - size -= n; - buf += n; - } - } - *buf++ = ','; - *buf++ = '\n'; - return buf - bufptr; -} - -static DEVICE_ATTR(modalias, 0444, print_cpu_modalias, NULL); - static __cpuinit int cpuid_device_create(int cpu) { struct device *dev; - int err; dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu), NULL, "cpu%d", cpu); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - err = device_create_file(dev, &dev_attr_modalias); - if (err) { - /* keep device around on error. attribute is optional. */ - err = 0; - } - - return 0; + return IS_ERR(dev) ? PTR_ERR(dev) : 0; } static void cpuid_device_destroy(int cpu) @@ -227,17 +182,6 @@ static char *cpuid_devnode(struct device *dev, umode_t *mode) return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } -static int cpuid_dev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (buf) { - print_cpu_modalias(NULL, NULL, buf); - add_uevent_var(env, "MODALIAS=%s", buf); - kfree(buf); - } - return 0; -} - static int __init cpuid_init(void) { int i, err = 0; @@ -256,7 +200,6 @@ static int __init cpuid_init(void) goto out_chrdev; } cpuid_class->devnode = cpuid_devnode; - cpuid_class->dev_uevent = cpuid_dev_uevent; for_each_online_cpu(i) { err = cpuid_device_create(i); if (err != 0) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index db87e78d7459..2a0c670c281d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "base.h" @@ -223,6 +224,9 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->node_id = cpu_to_node(num); cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + cpu->dev.bus->uevent = arch_cpu_uevent; +#endif error = device_register(&cpu->dev); if (!error && cpu->hotpluggable) register_cpu_control(cpu); @@ -247,6 +251,10 @@ struct device *get_cpu_device(unsigned cpu) } EXPORT_SYMBOL_GPL(get_cpu_device); +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +static DEVICE_ATTR(modalias, 0444, arch_print_cpu_modalias, NULL); +#endif + static struct attribute *cpu_root_attrs[] = { #ifdef CONFIG_ARCH_CPU_PROBE_RELEASE &dev_attr_probe.attr, @@ -257,6 +265,9 @@ static struct attribute *cpu_root_attrs[] = { &cpu_attrs[2].attr.attr, &dev_attr_kernel_max.attr, &dev_attr_offline.attr, +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE + &dev_attr_modalias.attr, +#endif NULL }; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 1f6587590a1a..6e53b4823d7f 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -44,6 +44,13 @@ extern ssize_t arch_cpu_release(const char *, size_t); #endif struct notifier_block; +#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE +extern int arch_cpu_uevent(struct device *dev, struct kobj_uevent_env *env); +extern ssize_t arch_print_cpu_modalias(struct device *dev, + struct device_attribute *attr, + char *bufptr); +#endif + /* * CPU notifier priorities. */ -- cgit v1.2.3 From 4f03a2c934894f30a64d397df8c7c4de129c5b30 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Jan 2012 15:55:57 -0800 Subject: drivers: hv: kvp: Add/cleanup connector defines The current KVP code carries some private connector related defines. Update connector.h to have all the KVP defines. As part of this patch get rid of some unused defines. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.h | 3 --- include/linux/connector.h | 1 + tools/hv/hv_kvp_daemon.c | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h index 9b765d7df838..c2c5bba25a5a 100644 --- a/drivers/hv/hv_kvp.h +++ b/drivers/hv/hv_kvp.h @@ -107,9 +107,6 @@ * the KVP user-mode component. */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ - enum hv_ku_op { KVP_REGISTER = 0, /* Register the user mode component */ KVP_KERNEL_GET, /* Kernel is requesting the value */ diff --git a/include/linux/connector.h b/include/linux/connector.h index 3c9c54fd5690..76384074262d 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -43,6 +43,7 @@ #define CN_IDX_DRBD 0x8 #define CN_VAL_DRBD 0x1 #define CN_KVP_IDX 0x9 /* HyperV KVP */ +#define CN_KVP_VAL 0x1 /* queries from the kernel */ #define CN_NETLINK_USERS 10 /* Highest index + 1 */ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 11224eddcdc2..2b6a2d950b88 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -40,15 +40,11 @@ #include /* - * KYS: TODO. Need to register these in the kernel. * * The following definitions are shared with the in-kernel component; do not * change any of this without making the corresponding changes in * the KVP kernel component. */ -#define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ -#define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ -#define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ /* * KVP protocol: The user mode component first registers with the -- cgit v1.2.3 From 2939437ce8f2de07237eb2bcce29b6a699bfe799 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 27 Jan 2012 15:55:58 -0800 Subject: drivers: hv: kvp: Move the contents of hv_kvp.h to hyperv.h In preparation for consolidating all KVP related defines into a single header file that both the kernel and user level components can use, move the contents of hv_kvp.h into hyperv.h. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 2 - drivers/hv/hv_kvp.h | 181 ------------------------------------------------- drivers/hv/hv_util.c | 3 - include/linux/hyperv.h | 165 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 186 deletions(-) delete mode 100644 drivers/hv/hv_kvp.h (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0e8343f585bb..4a6971e13539 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -28,8 +28,6 @@ #include #include -#include "hv_kvp.h" - /* diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h deleted file mode 100644 index c2c5bba25a5a..000000000000 --- a/drivers/hv/hv_kvp.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * An implementation of HyperV key value pair (KVP) functionality for Linux. - * - * - * Copyright (C) 2010, Novell, Inc. - * Author : K. Y. Srinivasan - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#ifndef _KVP_H -#define _KVP_H_ - -/* - * Maximum value size - used for both key names and value data, and includes - * any applicable NULL terminators. - * - * Note: This limit is somewhat arbitrary, but falls easily within what is - * supported for all native guests (back to Win 2000) and what is reasonable - * for the IC KVP exchange functionality. Note that Windows Me/98/95 are - * limited to 255 character key names. - * - * MSDN recommends not storing data values larger than 2048 bytes in the - * registry. - * - * Note: This value is used in defining the KVP exchange message - this value - * cannot be modified without affecting the message size and compatibility. - */ - -/* - * bytes, including any null terminators - */ -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) - - -/* - * Maximum key size - the registry limit for the length of an entry name - * is 256 characters, including the null terminator - */ - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) - -/* - * In Linux, we implement the KVP functionality in two components: - * 1) The kernel component which is packaged as part of the hv_utils driver - * is responsible for communicating with the host and responsible for - * implementing the host/guest protocol. 2) A user level daemon that is - * responsible for data gathering. - * - * Host/Guest Protocol: The host iterates over an index and expects the guest - * to assign a key name to the index and also return the value corresponding to - * the key. The host will have atmost one KVP transaction outstanding at any - * given point in time. The host side iteration stops when the guest returns - * an error. Microsoft has specified the following mapping of key names to - * host specified index: - * - * Index Key Name - * 0 FullyQualifiedDomainName - * 1 IntegrationServicesVersion - * 2 NetworkAddressIPv4 - * 3 NetworkAddressIPv6 - * 4 OSBuildNumber - * 5 OSName - * 6 OSMajorVersion - * 7 OSMinorVersion - * 8 OSVersion - * 9 ProcessorArchitecture - * - * The Windows host expects the Key Name and Key Value to be encoded in utf16. - * - * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the - * data gathering functionality in a user mode daemon. The user level daemon - * is also responsible for binding the key name to the index as well. The - * kernel and user-level daemon communicate using a connector channel. - * - * The user mode component first registers with the - * the kernel component. Subsequently, the kernel component requests, data - * for the specified keys. In response to this message the user mode component - * fills in the value corresponding to the specified key. We overload the - * sequence field in the cn_msg header to define our KVP message types. - * - * - * The kernel component simply acts as a conduit for communication between the - * Windows host and the user-level daemon. The kernel component passes up the - * index received from the Host to the user-level daemon. If the index is - * valid (supported), the corresponding key as well as its - * value (both are strings) is returned. If the index is invalid - * (not supported), a NULL key string is returned. - */ - -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - -#ifdef __KERNEL__ - -/* - * Registry value types. - */ - -#define REG_SZ 1 - -enum hv_kvp_exchg_op { - KVP_OP_GET = 0, - KVP_OP_SET, - KVP_OP_DELETE, - KVP_OP_ENUMERATE, - KVP_OP_COUNT /* Number of operations, must be last. */ -}; - -enum hv_kvp_exchg_pool { - KVP_POOL_EXTERNAL = 0, - KVP_POOL_GUEST, - KVP_POOL_AUTO, - KVP_POOL_AUTO_EXTERNAL, - KVP_POOL_AUTO_INTERNAL, - KVP_POOL_COUNT /* Number of pools, must be last. */ -}; - -struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; - -struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; - -struct hv_kvp_msg_enumerate { - u32 index; - struct hv_kvp_exchg_msg_value data; -}; - -struct hv_kvp_msg { - struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; -}; - -int hv_kvp_init(struct hv_util_service *); -void hv_kvp_deinit(void); -void hv_kvp_onchannelcallback(void *); - -#endif /* __KERNEL__ */ -#endif /* _KVP_H */ - diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 55d58f21e6d4..dbb8b8eec210 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -28,9 +28,6 @@ #include #include -#include "hv_kvp.h" - - static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 62b908e0e591..7332b3faecc8 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -25,6 +25,166 @@ #ifndef _HYPERV_H #define _HYPERV_H +#include + +/* + * An implementation of HyperV key value pair (KVP) functionality for Linux. + * + * + * Copyright (C) 2010, Novell, Inc. + * Author : K. Y. Srinivasan + * + */ + +/* + * Maximum value size - used for both key names and value data, and includes + * any applicable NULL terminators. + * + * Note: This limit is somewhat arbitrary, but falls easily within what is + * supported for all native guests (back to Win 2000) and what is reasonable + * for the IC KVP exchange functionality. Note that Windows Me/98/95 are + * limited to 255 character key names. + * + * MSDN recommends not storing data values larger than 2048 bytes in the + * registry. + * + * Note: This value is used in defining the KVP exchange message - this value + * cannot be modified without affecting the message size and compatibility. + */ + +/* + * bytes, including any null terminators + */ +#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE (2048) + + +/* + * Maximum key size - the registry limit for the length of an entry name + * is 256 characters, including the null terminator + */ + +#define HV_KVP_EXCHANGE_MAX_KEY_SIZE (512) + +/* + * In Linux, we implement the KVP functionality in two components: + * 1) The kernel component which is packaged as part of the hv_utils driver + * is responsible for communicating with the host and responsible for + * implementing the host/guest protocol. 2) A user level daemon that is + * responsible for data gathering. + * + * Host/Guest Protocol: The host iterates over an index and expects the guest + * to assign a key name to the index and also return the value corresponding to + * the key. The host will have atmost one KVP transaction outstanding at any + * given point in time. The host side iteration stops when the guest returns + * an error. Microsoft has specified the following mapping of key names to + * host specified index: + * + * Index Key Name + * 0 FullyQualifiedDomainName + * 1 IntegrationServicesVersion + * 2 NetworkAddressIPv4 + * 3 NetworkAddressIPv6 + * 4 OSBuildNumber + * 5 OSName + * 6 OSMajorVersion + * 7 OSMinorVersion + * 8 OSVersion + * 9 ProcessorArchitecture + * + * The Windows host expects the Key Name and Key Value to be encoded in utf16. + * + * Guest Kernel/KVP Daemon Protocol: As noted earlier, we implement all of the + * data gathering functionality in a user mode daemon. The user level daemon + * is also responsible for binding the key name to the index as well. The + * kernel and user-level daemon communicate using a connector channel. + * + * The user mode component first registers with the + * the kernel component. Subsequently, the kernel component requests, data + * for the specified keys. In response to this message the user mode component + * fills in the value corresponding to the specified key. We overload the + * sequence field in the cn_msg header to define our KVP message types. + * + * + * The kernel component simply acts as a conduit for communication between the + * Windows host and the user-level daemon. The kernel component passes up the + * index received from the Host to the user-level daemon. If the index is + * valid (supported), the corresponding key as well as its + * value (both are strings) is returned. If the index is invalid + * (not supported), a NULL key string is returned. + */ + +/* + * + * The following definitions are shared with the user-mode component; do not + * change any of this without making the corresponding changes in + * the KVP user-mode component. + */ + +enum hv_ku_op { + KVP_REGISTER = 0, /* Register the user mode component */ + KVP_KERNEL_GET, /* Kernel is requesting the value */ + KVP_KERNEL_SET, /* Kernel is providing the value */ + KVP_USER_GET, /* User is requesting the value */ + KVP_USER_SET /* User is providing the value */ +}; + +struct hv_ku_msg { + __u32 kvp_index; /* Key index */ + __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ + __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ +}; + + + + +#ifdef __KERNEL__ + +/* + * Registry value types. + */ + +#define REG_SZ 1 + +enum hv_kvp_exchg_op { + KVP_OP_GET = 0, + KVP_OP_SET, + KVP_OP_DELETE, + KVP_OP_ENUMERATE, + KVP_OP_COUNT /* Number of operations, must be last. */ +}; + +enum hv_kvp_exchg_pool { + KVP_POOL_EXTERNAL = 0, + KVP_POOL_GUEST, + KVP_POOL_AUTO, + KVP_POOL_AUTO_EXTERNAL, + KVP_POOL_AUTO_INTERNAL, + KVP_POOL_COUNT /* Number of pools, must be last. */ +}; + +struct hv_kvp_hdr { + u8 operation; + u8 pool; +}; + +struct hv_kvp_exchg_msg_value { + u32 value_type; + u32 key_size; + u32 value_size; + u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct hv_kvp_msg_enumerate { + u32 index; + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg { + struct hv_kvp_hdr kvp_hdr; + struct hv_kvp_msg_enumerate kvp_data; +}; + #include #include #include @@ -870,4 +1030,9 @@ struct hyperv_service_callback { extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, struct icmsg_negotiate *, u8 *); +int hv_kvp_init(struct hv_util_service *); +void hv_kvp_deinit(void); +void hv_kvp_onchannelcallback(void *); + +#endif /* __KERNEL__ */ #endif /* _HYPERV_H */ -- cgit v1.2.3 From 59a084a70afa0678bda2a23a7bc7cc59664945c7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:48 -0800 Subject: drivers: hv: Cleanup the kvp related state in hyperv.h Now cleanup the hyperv.h with regards to KVP definitions. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- include/linux/hyperv.h | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7332b3faecc8..b822978ecbc8 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -137,7 +137,6 @@ struct hv_ku_msg { -#ifdef __KERNEL__ /* * Registry value types. @@ -163,28 +162,30 @@ enum hv_kvp_exchg_pool { }; struct hv_kvp_hdr { - u8 operation; - u8 pool; -}; + __u8 operation; + __u8 pool; + __u16 pad; +} __attribute__((packed)); struct hv_kvp_exchg_msg_value { - u32 value_type; - u32 key_size; - u32 value_size; - u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; -}; + __u32 value_type; + __u32 key_size; + __u32 value_size; + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +} __attribute__((packed)); struct hv_kvp_msg_enumerate { - u32 index; + __u32 index; struct hv_kvp_exchg_msg_value data; -}; +} __attribute__((packed)); struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; struct hv_kvp_msg_enumerate kvp_data; -}; +} __attribute__((packed)); +#ifdef __KERNEL__ #include #include #include -- cgit v1.2.3 From 2640335438ca4d7b139e114dae5f0d80e740e106 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:50 -0800 Subject: drivers: hv: kvp: Cleanup the kernel/user protocol Now, cleanup the user/kernel KVP protocol by using the same structure definition that is used for host/guest KVP protocol. This simplifies the code. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 41 +++++++++++++++++++++++++---------------- include/linux/hyperv.h | 30 +++++------------------------- tools/hv/hv_kvp_daemon.c | 30 +++++++++++++++--------------- 3 files changed, 45 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 4a6971e13539..0ef4c1f6ca54 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -71,15 +71,20 @@ kvp_register(void) { struct cn_msg *msg; + struct hv_kvp_msg *kvp_msg; + char *version; - msg = kzalloc(sizeof(*msg) + strlen(HV_DRV_VERSION) + 1 , GFP_ATOMIC); + msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg), GFP_ATOMIC); if (msg) { + kvp_msg = (struct hv_kvp_msg *)msg->data; + version = kvp_msg->body.kvp_version; msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - msg->seq = KVP_REGISTER; - strcpy(msg->data, HV_DRV_VERSION); - msg->len = strlen(HV_DRV_VERSION) + 1; + + kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER; + strcpy(version, HV_DRV_VERSION); + msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); kfree(msg); } @@ -101,23 +106,24 @@ kvp_work_func(struct work_struct *dummy) static void kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { - struct hv_ku_msg *message; + struct hv_kvp_msg *message; + struct hv_kvp_msg_enumerate *data; - message = (struct hv_ku_msg *)msg->data; - if (msg->seq == KVP_REGISTER) { + message = (struct hv_kvp_msg *)msg->data; + if (message->kvp_hdr.operation == KVP_OP_REGISTER) { pr_info("KVP: user-mode registering done.\n"); kvp_register(); } - if (msg->seq == KVP_USER_SET) { + if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { + data = &message->body.kvp_enum_data; /* * Complete the transaction by forwarding the key value * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(message->kvp_key, - message->kvp_value, - !strlen(message->kvp_key)); + kvp_respond_to_host(data->data.key, data->data.value, + !strlen(data->data.key)); } } @@ -125,6 +131,7 @@ static void kvp_send_key(struct work_struct *dummy) { struct cn_msg *msg; + struct hv_kvp_msg *message; int index = kvp_transaction.index; msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); @@ -132,9 +139,11 @@ kvp_send_key(struct work_struct *dummy) if (msg) { msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; - msg->seq = KVP_KERNEL_GET; - ((struct hv_ku_msg *)msg->data)->kvp_index = index; - msg->len = sizeof(struct hv_ku_msg); + + message = (struct hv_kvp_msg *)msg->data; + message->kvp_hdr.operation = KVP_OP_ENUMERATE; + message->body.kvp_enum_data.index = index; + msg->len = sizeof(struct hv_kvp_msg); cn_netlink_send(msg, 0, GFP_ATOMIC); kfree(msg); } @@ -191,7 +200,7 @@ kvp_respond_to_host(char *key, char *value, int error) kvp_msg = (struct hv_kvp_msg *) &recv_buffer[sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; + kvp_data = &kvp_msg->body.kvp_enum_data; key_name = key; /* @@ -266,7 +275,7 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->kvp_data; + kvp_data = &kvp_msg->body.kvp_enum_data; /* * We only support the "get" operation on diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index b822978ecbc8..75aee6720c1b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -113,30 +113,6 @@ * (not supported), a NULL key string is returned. */ -/* - * - * The following definitions are shared with the user-mode component; do not - * change any of this without making the corresponding changes in - * the KVP user-mode component. - */ - -enum hv_ku_op { - KVP_REGISTER = 0, /* Register the user mode component */ - KVP_KERNEL_GET, /* Kernel is requesting the value */ - KVP_KERNEL_SET, /* Kernel is providing the value */ - KVP_USER_GET, /* User is requesting the value */ - KVP_USER_SET /* User is providing the value */ -}; - -struct hv_ku_msg { - __u32 kvp_index; /* Key index */ - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; - - - - /* * Registry value types. @@ -149,6 +125,7 @@ enum hv_kvp_exchg_op { KVP_OP_SET, KVP_OP_DELETE, KVP_OP_ENUMERATE, + KVP_OP_REGISTER, KVP_OP_COUNT /* Number of operations, must be last. */ }; @@ -182,7 +159,10 @@ struct hv_kvp_msg_enumerate { struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; - struct hv_kvp_msg_enumerate kvp_data; + union { + struct hv_kvp_msg_enumerate kvp_enum_data; + char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + } body; } __attribute__((packed)); #ifdef __KERNEL__ diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index b75523cde2cd..4ebf70380582 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -302,7 +302,7 @@ int main(void) struct pollfd pfd; struct nlmsghdr *incoming_msg; struct cn_msg *incoming_cn_msg; - struct hv_ku_msg *hv_msg; + struct hv_kvp_msg *hv_msg; char *p; char *key_value; char *key_name; @@ -340,9 +340,11 @@ int main(void) message = (struct cn_msg *)kvp_send_buffer; message->id.idx = CN_KVP_IDX; message->id.val = CN_KVP_VAL; - message->seq = KVP_REGISTER; + + hv_msg = (struct hv_kvp_msg *)message->data; + hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; message->ack = 0; - message->len = 0; + message->len = sizeof(struct hv_kvp_msg); len = netlink_send(fd, message); if (len < 0) { @@ -368,14 +370,15 @@ int main(void) incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; - switch (incoming_cn_msg->seq) { - case KVP_REGISTER: + switch (hv_msg->kvp_hdr.operation) { + case KVP_OP_REGISTER: /* * Driver is registering with us; stash away the version * information. */ - p = (char *)incoming_cn_msg->data; + p = (char *)hv_msg->body.kvp_version; lic_version = malloc(strlen(p) + 1); if (lic_version) { strcpy(lic_version, p); @@ -386,17 +389,15 @@ int main(void) } continue; - case KVP_KERNEL_GET: - break; default: - continue; + break; } - hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; - key_name = (char *)hv_msg->kvp_key; - key_value = (char *)hv_msg->kvp_value; + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; + key_name = (char *)hv_msg->body.kvp_enum_data.data.key; + key_value = (char *)hv_msg->body.kvp_enum_data.data.value; - switch (hv_msg->kvp_index) { + switch (hv_msg->body.kvp_enum_data.index) { case FullyQualifiedDomainName: kvp_get_domain_name(key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE); @@ -456,9 +457,8 @@ int main(void) incoming_cn_msg->id.idx = CN_KVP_IDX; incoming_cn_msg->id.val = CN_KVP_VAL; - incoming_cn_msg->seq = KVP_USER_SET; incoming_cn_msg->ack = 0; - incoming_cn_msg->len = sizeof(struct hv_ku_msg); + incoming_cn_msg->len = sizeof(struct hv_kvp_msg); len = netlink_send(fd, incoming_cn_msg); if (len < 0) { -- cgit v1.2.3 From aad4f4000cecec9c80b5f9aff91043dc104d61a0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 18 Nov 2011 10:12:49 -0800 Subject: PCI: Add helper macro for pci_register_driver boilerplate This patch introduces the module_pci_driver macro which is a convenience macro for PCI driver modules similar to module_platform_driver. It is intended to be used by drivers which init/exit section does nothing but register/unregister the PCI driver. By using this macro it is possible to eliminate a few lines of boilerplate code per PCI driver. Based on work done by Lars-Peter Clausen for other busses (i2c and spi). Cc: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman Acked-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- include/linux/pci.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df3deff..d4afd703e948 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -946,6 +946,19 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *, __pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) void pci_unregister_driver(struct pci_driver *dev); + +/** + * module_pci_driver() - Helper macro for registering a PCI driver + * @__pci_driver: pci_driver struct + * + * Helper macro for PCI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_pci_driver(__pci_driver) \ + module_driver(__pci_driver, pci_register_driver, \ + pci_unregister_driver) + void pci_remove_behind_bridge(struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev); int pci_add_dynid(struct pci_driver *drv, -- cgit v1.2.3 From 74d1d82cdaaec727f5072eb1c9f49b7e920e076f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 6 Feb 2012 11:22:22 -0800 Subject: drivers/base: add bus for System-on-Chip devices Traditionally, any System-on-Chip based platform creates a flat list of platform_devices directly under /sys/devices/platform. In order to give these some better structure, this introduces a new bus type for soc_devices that are registered with the new soc_device_register() function. All devices that are on the same chip should then be registered as child devices of the soc device. The soc bus also exports a few standardised device attributes which allow user space to query the specific type of soc. Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/base/Kconfig | 3 + drivers/base/Makefile | 1 + drivers/base/soc.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sys_soc.h | 37 ++++++++++ 4 files changed, 224 insertions(+) create mode 100644 drivers/base/soc.c create mode 100644 include/linux/sys_soc.h (limited to 'include/linux') diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 7be9f79018e9..9aa618acfe97 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -176,6 +176,9 @@ config GENERIC_CPU_DEVICES bool default n +config SOC_BUS + bool + source "drivers/base/regmap/Kconfig" config DMA_SHARED_BUFFER diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 610f9997a403..b6d1b9c4200c 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MODULES) += module.o endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_REGMAP) += regmap/ +obj-$(CONFIG_SOC_BUS) += soc.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/soc.c b/drivers/base/soc.c new file mode 100644 index 000000000000..05f150382da8 --- /dev/null +++ b/drivers/base/soc.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Lee Jones for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_IDR(soc_ida); +static DEFINE_SPINLOCK(soc_lock); + +static ssize_t soc_info_get(struct device *dev, + struct device_attribute *attr, + char *buf); + +struct soc_device { + struct device dev; + struct soc_device_attribute *attr; + int soc_dev_num; +}; + +static struct bus_type soc_bus_type = { + .name = "soc", +}; + +static DEVICE_ATTR(machine, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(family, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(soc_id, S_IRUGO, soc_info_get, NULL); +static DEVICE_ATTR(revision, S_IRUGO, soc_info_get, NULL); + +struct device *soc_device_to_device(struct soc_device *soc_dev) +{ + return &soc_dev->dev; +} + +static mode_t soc_attribute_mode(struct kobject *kobj, + struct attribute *attr, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + if ((attr == &dev_attr_machine.attr) + && (soc_dev->attr->machine != NULL)) + return attr->mode; + if ((attr == &dev_attr_family.attr) + && (soc_dev->attr->family != NULL)) + return attr->mode; + if ((attr == &dev_attr_revision.attr) + && (soc_dev->attr->revision != NULL)) + return attr->mode; + if ((attr == &dev_attr_soc_id.attr) + && (soc_dev->attr->soc_id != NULL)) + return attr->mode; + + /* Unknown or unfilled attribute. */ + return 0; +} + +static ssize_t soc_info_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + if (attr == &dev_attr_machine) + return sprintf(buf, "%s\n", soc_dev->attr->machine); + if (attr == &dev_attr_family) + return sprintf(buf, "%s\n", soc_dev->attr->family); + if (attr == &dev_attr_revision) + return sprintf(buf, "%s\n", soc_dev->attr->revision); + if (attr == &dev_attr_soc_id) + return sprintf(buf, "%s\n", soc_dev->attr->soc_id); + + return -EINVAL; + +} + +static struct attribute *soc_attr[] = { + &dev_attr_machine.attr, + &dev_attr_family.attr, + &dev_attr_soc_id.attr, + &dev_attr_revision.attr, + NULL, +}; + +static const struct attribute_group soc_attr_group = { + .attrs = soc_attr, + .is_visible = soc_attribute_mode, +}; + +static const struct attribute_group *soc_attr_groups[] = { + &soc_attr_group, + NULL, +}; + +static void soc_release(struct device *dev) +{ + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + + kfree(soc_dev); +} + +struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) +{ + struct soc_device *soc_dev; + int ret; + + soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); + if (!soc_dev) { + ret = -ENOMEM; + goto out1; + } + + /* Fetch a unique (reclaimable) SOC ID. */ + do { + if (!ida_pre_get(&soc_ida, GFP_KERNEL)) { + ret = -ENOMEM; + goto out2; + } + + spin_lock(&soc_lock); + ret = ida_get_new(&soc_ida, &soc_dev->soc_dev_num); + spin_unlock(&soc_lock); + + } while (ret == -EAGAIN); + + if (ret) + goto out2; + + soc_dev->attr = soc_dev_attr; + soc_dev->dev.bus = &soc_bus_type; + soc_dev->dev.groups = soc_attr_groups; + soc_dev->dev.release = soc_release; + + dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num); + + ret = device_register(&soc_dev->dev); + if (ret) + goto out3; + + return soc_dev; + +out3: + ida_remove(&soc_ida, soc_dev->soc_dev_num); +out2: + kfree(soc_dev); +out1: + return ERR_PTR(ret); +} + +/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ +void soc_device_unregister(struct soc_device *soc_dev) +{ + ida_remove(&soc_ida, soc_dev->soc_dev_num); + + device_unregister(&soc_dev->dev); +} + +static int __init soc_bus_register(void) +{ + spin_lock_init(&soc_lock); + + return bus_register(&soc_bus_type); +} +core_initcall(soc_bus_register); + +static void __exit soc_bus_unregister(void) +{ + ida_destroy(&soc_ida); + + bus_unregister(&soc_bus_type); +} +module_exit(soc_bus_unregister); diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h new file mode 100644 index 000000000000..2739ccb69571 --- /dev/null +++ b/include/linux/sys_soc.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * Author: Lee Jones for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ +#ifndef __SOC_BUS_H +#define __SOC_BUS_H + +#include + +struct soc_device_attribute { + const char *machine; + const char *family; + const char *revision; + const char *soc_id; +}; + +/** + * soc_device_register - register SoC as a device + * @soc_plat_dev_attr: Attributes passed from platform to be attributed to a SoC + */ +struct soc_device *soc_device_register( + struct soc_device_attribute *soc_plat_dev_attr); + +/** + * soc_device_unregister - unregister SoC device + * @dev: SoC device to be unregistered + */ +void soc_device_unregister(struct soc_device *soc_dev); + +/** + * soc_device_to_device - helper function to fetch struct device + * @soc: Previously registered SoC device container + */ +struct device *soc_device_to_device(struct soc_device *soc); + +#endif /* __SOC_BUS_H */ -- cgit v1.2.3 From d1c3414c2a9d10ef7f0f7665f5d2947cd088c093 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 5 Mar 2012 08:47:41 -0700 Subject: drivercore: Add driver probe deferral mechanism Allow drivers to report at probe time that they cannot get all the resources required by the device, and should be retried at a later time. This should completely solve the problem of getting devices initialized in the right order. Right now this is mostly handled by mucking about with initcall ordering which is a complete hack, and doesn't even remotely handle the case where device drivers are in modules. This approach completely sidesteps the issues by allowing driver registration to occur in any order, and any driver can request to be retried after a few more other drivers get probed. v4: - Integrate Manjunath's addition of a separate workqueue - Change -EAGAIN to -EPROBE_DEFER for drivers to trigger deferral - Update comment blocks to reflect how the code really works v3: - Hold off workqueue scheduling until late_initcall so that the bulk of driver probes are complete before we start retrying deferred devices. - Tested with simple use cases. Still needs more testing though. Using it to get rid of the gpio early_initcall madness, or to replace the ASoC internal probe deferral code would be ideal. v2: - added locking so it should no longer be utterly broken in that regard - remove device from deferred list at device_del time. - Still completely untested with any real use case, but has been boot tested. Signed-off-by: Grant Likely Cc: Mark Brown Cc: Arnd Bergmann Cc: Dilan Lee Cc: Manjunath GKondaiah Cc: Alan Stern Cc: Tony Lindgren Cc: Alan Cox Reviewed-by: Mark Brown Acked-by: David Daney Reviewed-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 1 + drivers/base/core.c | 2 + drivers/base/dd.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/device.h | 5 ++ include/linux/errno.h | 1 + 5 files changed, 146 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index b858dfd9a37c..2c13deae5f82 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -105,6 +105,7 @@ extern void bus_remove_driver(struct device_driver *drv); extern void driver_detach(struct device_driver *drv); extern int driver_probe_device(struct device_driver *drv, struct device *dev); +extern void driver_deferred_probe_del(struct device *dev); static inline int driver_match_device(struct device_driver *drv, struct device *dev) { diff --git a/drivers/base/core.c b/drivers/base/core.c index 74dda4f697f9..d4ff7adce38c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,6 +644,7 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); + INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -1188,6 +1189,7 @@ void device_del(struct device *dev) device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); bus_remove_device(dev); + driver_deferred_probe_del(dev); /* * Some platform devices are driven without driver attached diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 142e3d600f14..442b7641a086 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -28,6 +28,133 @@ #include "base.h" #include "power/power.h" +/* + * Deferred Probe infrastructure. + * + * Sometimes driver probe order matters, but the kernel doesn't always have + * dependency information which means some drivers will get probed before a + * resource it depends on is available. For example, an SDHCI driver may + * first need a GPIO line from an i2c GPIO controller before it can be + * initialized. If a required resource is not available yet, a driver can + * request probing to be deferred by returning -EPROBE_DEFER from its probe hook + * + * Deferred probe maintains two lists of devices, a pending list and an active + * list. A driver returning -EPROBE_DEFER causes the device to be added to the + * pending list. A successful driver probe will trigger moving all devices + * from the pending to the active list so that the workqueue will eventually + * retry them. + * + * The deferred_probe_mutex must be held any time the deferred_probe_*_list + * of the (struct device*)->deferred_probe pointers are manipulated + */ +static DEFINE_MUTEX(deferred_probe_mutex); +static LIST_HEAD(deferred_probe_pending_list); +static LIST_HEAD(deferred_probe_active_list); +static struct workqueue_struct *deferred_wq; + +/** + * deferred_probe_work_func() - Retry probing devices in the active list. + */ +static void deferred_probe_work_func(struct work_struct *work) +{ + struct device *dev; + /* + * This block processes every device in the deferred 'active' list. + * Each device is removed from the active list and passed to + * bus_probe_device() to re-attempt the probe. The loop continues + * until every device in the active list is removed and retried. + * + * Note: Once the device is removed from the list and the mutex is + * released, it is possible for the device get freed by another thread + * and cause a illegal pointer dereference. This code uses + * get/put_device() to ensure the device structure cannot disappear + * from under our feet. + */ + mutex_lock(&deferred_probe_mutex); + while (!list_empty(&deferred_probe_active_list)) { + dev = list_first_entry(&deferred_probe_active_list, + typeof(*dev), deferred_probe); + list_del_init(&dev->deferred_probe); + + get_device(dev); + + /* Drop the mutex while probing each device; the probe path + * may manipulate the deferred list */ + mutex_unlock(&deferred_probe_mutex); + dev_dbg(dev, "Retrying from deferred list\n"); + bus_probe_device(dev); + mutex_lock(&deferred_probe_mutex); + + put_device(dev); + } + mutex_unlock(&deferred_probe_mutex); +} +static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); + +static void driver_deferred_probe_add(struct device *dev) +{ + mutex_lock(&deferred_probe_mutex); + if (list_empty(&dev->deferred_probe)) { + dev_dbg(dev, "Added to deferred list\n"); + list_add(&dev->deferred_probe, &deferred_probe_pending_list); + } + mutex_unlock(&deferred_probe_mutex); +} + +void driver_deferred_probe_del(struct device *dev) +{ + mutex_lock(&deferred_probe_mutex); + if (!list_empty(&dev->deferred_probe)) { + dev_dbg(dev, "Removed from deferred list\n"); + list_del_init(&dev->deferred_probe); + } + mutex_unlock(&deferred_probe_mutex); +} + +static bool driver_deferred_probe_enable = false; +/** + * driver_deferred_probe_trigger() - Kick off re-probing deferred devices + * + * This functions moves all devices from the pending list to the active + * list and schedules the deferred probe workqueue to process them. It + * should be called anytime a driver is successfully bound to a device. + */ +static void driver_deferred_probe_trigger(void) +{ + if (!driver_deferred_probe_enable) + return; + + /* A successful probe means that all the devices in the pending list + * should be triggered to be reprobed. Move all the deferred devices + * into the active list so they can be retried by the workqueue */ + mutex_lock(&deferred_probe_mutex); + list_splice_tail_init(&deferred_probe_pending_list, + &deferred_probe_active_list); + mutex_unlock(&deferred_probe_mutex); + + /* Kick the re-probe thread. It may already be scheduled, but + * it is safe to kick it again. */ + queue_work(deferred_wq, &deferred_probe_work); +} + +/** + * deferred_probe_initcall() - Enable probing of deferred devices + * + * We don't want to get in the way when the bulk of drivers are getting probed. + * Instead, this initcall makes sure that deferred probing is delayed until + * late_initcall time. + */ +static int deferred_probe_initcall(void) +{ + deferred_wq = create_singlethread_workqueue("deferwq"); + if (WARN_ON(!deferred_wq)) + return -ENOMEM; + + driver_deferred_probe_enable = true; + driver_deferred_probe_trigger(); + return 0; +} +late_initcall(deferred_probe_initcall); static void driver_bound(struct device *dev) { @@ -42,6 +169,11 @@ static void driver_bound(struct device *dev) klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + /* Make sure the device is no longer in one of the deferred lists + * and kick off retrying all pending devices */ + driver_deferred_probe_del(dev); + driver_deferred_probe_trigger(); + if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); @@ -142,7 +274,11 @@ probe_failed: driver_sysfs_remove(dev); dev->driver = NULL; - if (ret != -ENODEV && ret != -ENXIO) { + if (ret == -EPROBE_DEFER) { + /* Driver requested deferred probing */ + dev_info(dev, "Driver %s requests probe deferral\n", drv->name); + driver_deferred_probe_add(dev); + } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", diff --git a/include/linux/device.h b/include/linux/device.h index f62e21689fdd..22d6938ddbb4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -585,6 +585,10 @@ struct device_dma_parameters { * @mutex: Mutex to synchronize calls to its driver. * @bus: Type of bus device is on. * @driver: Which driver has allocated this + * @deferred_probe: entry in deferred_probe_list which is used to retry the + * binding of drivers which were unable to get all the resources + * needed by the device; typically because it depends on another + * driver getting probed first. * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point @@ -644,6 +648,7 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ + struct list_head deferred_probe; void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; diff --git a/include/linux/errno.h b/include/linux/errno.h index 46685832ed99..2d09bfa5c262 100644 --- a/include/linux/errno.h +++ b/include/linux/errno.h @@ -16,6 +16,7 @@ #define ERESTARTNOHAND 514 /* restart if no handler.. */ #define ENOIOCTLCMD 515 /* No ioctl command */ #define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */ +#define EPROBE_DEFER 517 /* Driver requests probe retry */ /* Defined for the NFSv3 protocol */ #define EBADHANDLE 521 /* Illegal NFS file handle */ -- cgit v1.2.3 From ef8a3fd6e5e12e8989dae97ba5491c2e39369af9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Mar 2012 12:17:22 -0800 Subject: driver core: move the deferred probe pointer into the private area Nothing outside of the driver core needs to get to the deferred probe pointer, so move it inside the private area of 'struct device' so no one tries to mess around with it. Cc: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/core.c | 2 +- drivers/base/dd.c | 18 ++++++++++-------- include/linux/device.h | 5 ----- 4 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c13deae5f82..6ee17bb391a9 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -59,6 +59,10 @@ struct driver_private { * @knode_parent - node in sibling list * @knode_driver - node in driver list * @knode_bus - node in bus list + * @deferred_probe - entry in deferred_probe_list which is used to retry the + * binding of drivers which were unable to get all the resources needed by + * the device; typically because it depends on another driver getting + * probed first. * @driver_data - private pointer for driver specific info. Will turn into a * list soon. * @device - pointer back to the struct class that this structure is @@ -71,6 +75,7 @@ struct device_private { struct klist_node knode_parent; struct klist_node knode_driver; struct klist_node knode_bus; + struct list_head deferred_probe; void *driver_data; struct device *device; }; diff --git a/drivers/base/core.c b/drivers/base/core.c index d4ff7adce38c..7050a75dde38 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -644,7 +644,6 @@ void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); - INIT_LIST_HEAD(&dev->deferred_probe); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); @@ -922,6 +921,7 @@ int device_private_init(struct device *dev) dev->p->device = dev; klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); + INIT_LIST_HEAD(&dev->p->deferred_probe); return 0; } diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 442b7641a086..9fa888e08059 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -45,7 +45,7 @@ * retry them. * * The deferred_probe_mutex must be held any time the deferred_probe_*_list - * of the (struct device*)->deferred_probe pointers are manipulated + * of the (struct device*)->p->deferred_probe pointers are manipulated */ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); @@ -58,6 +58,7 @@ static struct workqueue_struct *deferred_wq; static void deferred_probe_work_func(struct work_struct *work) { struct device *dev; + struct device_private *private; /* * This block processes every device in the deferred 'active' list. * Each device is removed from the active list and passed to @@ -72,9 +73,10 @@ static void deferred_probe_work_func(struct work_struct *work) */ mutex_lock(&deferred_probe_mutex); while (!list_empty(&deferred_probe_active_list)) { - dev = list_first_entry(&deferred_probe_active_list, - typeof(*dev), deferred_probe); - list_del_init(&dev->deferred_probe); + private = list_first_entry(&deferred_probe_active_list, + typeof(*dev->p), deferred_probe); + dev = private->device; + list_del_init(&private->deferred_probe); get_device(dev); @@ -94,9 +96,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); static void driver_deferred_probe_add(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (list_empty(&dev->deferred_probe)) { + if (list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Added to deferred list\n"); - list_add(&dev->deferred_probe, &deferred_probe_pending_list); + list_add(&dev->p->deferred_probe, &deferred_probe_pending_list); } mutex_unlock(&deferred_probe_mutex); } @@ -104,9 +106,9 @@ static void driver_deferred_probe_add(struct device *dev) void driver_deferred_probe_del(struct device *dev) { mutex_lock(&deferred_probe_mutex); - if (!list_empty(&dev->deferred_probe)) { + if (!list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Removed from deferred list\n"); - list_del_init(&dev->deferred_probe); + list_del_init(&dev->p->deferred_probe); } mutex_unlock(&deferred_probe_mutex); } diff --git a/include/linux/device.h b/include/linux/device.h index 22d6938ddbb4..f62e21689fdd 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -585,10 +585,6 @@ struct device_dma_parameters { * @mutex: Mutex to synchronize calls to its driver. * @bus: Type of bus device is on. * @driver: Which driver has allocated this - * @deferred_probe: entry in deferred_probe_list which is used to retry the - * binding of drivers which were unable to get all the resources - * needed by the device; typically because it depends on another - * driver getting probed first. * @platform_data: Platform data specific to the device. * Example: For devices on custom boards, as typical of embedded * and SOC based hardware, Linux often uses platform_data to point @@ -648,7 +644,6 @@ struct device { struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ - struct list_head deferred_probe; void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; -- cgit v1.2.3 From e485ceac9ebd43901ef0ce13622385d509e072e7 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sat, 10 Mar 2012 15:32:08 -0800 Subject: Drivers: hv: Add new message types to enhance KVP Add additional KVP (Key Value Pair) protocol messages to enhance KVP functionality for Linux guests on Hyper-V. As part of this, patch define an explicit version negoitiation message. Reviewed-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 5 +++-- include/linux/hyperv.h | 30 +++++++++++++++++++++++++++--- tools/hv/hv_kvp_daemon.c | 2 +- 3 files changed, 31 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0ef4c1f6ca54..779109b6f4f0 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -78,7 +78,7 @@ kvp_register(void) if (msg) { kvp_msg = (struct hv_kvp_msg *)msg->data; - version = kvp_msg->body.kvp_version; + version = kvp_msg->body.kvp_register.version; msg->id.idx = CN_KVP_IDX; msg->id.val = CN_KVP_VAL; @@ -122,7 +122,8 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) * to the host. But first, cancel the timeout. */ if (cancel_delayed_work_sync(&kvp_work)) - kvp_respond_to_host(data->data.key, data->data.value, + kvp_respond_to_host(data->data.key, + data->data.value, !strlen(data->data.key)); } } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e57a6c6ee0e8..a2d8c547f91b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -149,7 +149,11 @@ struct hv_kvp_exchg_msg_value { __u32 key_size; __u32 value_size; __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; - __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + union { + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; + __u32 value_u32; + __u64 value_u64; + }; } __attribute__((packed)); struct hv_kvp_msg_enumerate { @@ -157,11 +161,31 @@ struct hv_kvp_msg_enumerate { struct hv_kvp_exchg_msg_value data; } __attribute__((packed)); +struct hv_kvp_msg_get { + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg_set { + struct hv_kvp_exchg_msg_value data; +}; + +struct hv_kvp_msg_delete { + __u32 key_size; + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +}; + +struct hv_kvp_register { + __u8 version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; +}; + struct hv_kvp_msg { struct hv_kvp_hdr kvp_hdr; union { - struct hv_kvp_msg_enumerate kvp_enum_data; - char kvp_version[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + struct hv_kvp_msg_get kvp_get; + struct hv_kvp_msg_set kvp_set; + struct hv_kvp_msg_delete kvp_delete; + struct hv_kvp_msg_enumerate kvp_enum_data; + struct hv_kvp_register kvp_register; } body; } __attribute__((packed)); diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 4ebf70380582..00d3f7c099e0 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -378,7 +378,7 @@ int main(void) * Driver is registering with us; stash away the version * information. */ - p = (char *)hv_msg->body.kvp_version; + p = (char *)hv_msg->body.kvp_register.version; lic_version = malloc(strlen(p) + 1); if (lic_version) { strcpy(lic_version, p); -- cgit v1.2.3 From fa3d5b85c681518b6e4ec515814dcb2d5b702b89 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 16 Mar 2012 08:02:25 -0700 Subject: Drivers: hv: Support the newly introduced KVP messages in the driver Support the newly defined KVP message types. It turns out that the host pushes a set of standard key value pairs as soon as the guest opens the KVP channel. Since we cannot handle these tuples until the user level daemon loads up, defer reading the KVP channel until the user level daemon is launched. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 218 ++++++++++++++++++++++++++++++++++++----------- include/linux/hyperv.h | 2 + tools/hv/hv_kvp_daemon.c | 7 ++ 3 files changed, 176 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 779109b6f4f0..cfe60b02e3e8 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -42,9 +42,10 @@ static struct { bool active; /* transaction status - active or not */ int recv_len; /* number of bytes received. */ - int index; /* current index */ + struct hv_kvp_msg *kvp_msg; /* current message */ struct vmbus_channel *recv_channel; /* chn we got the request */ u64 recv_req_id; /* request ID. */ + void *kvp_context; /* for the channel callback */ } kvp_transaction; static void kvp_send_key(struct work_struct *dummy); @@ -110,12 +111,15 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) struct hv_kvp_msg_enumerate *data; message = (struct hv_kvp_msg *)msg->data; - if (message->kvp_hdr.operation == KVP_OP_REGISTER) { + switch (message->kvp_hdr.operation) { + case KVP_OP_REGISTER: pr_info("KVP: user-mode registering done.\n"); kvp_register(); - } + kvp_transaction.active = false; + hv_kvp_onchannelcallback(kvp_transaction.kvp_context); + break; - if (message->kvp_hdr.operation == KVP_OP_ENUMERATE) { + default: data = &message->body.kvp_enum_data; /* * Complete the transaction by forwarding the key value @@ -133,21 +137,104 @@ kvp_send_key(struct work_struct *dummy) { struct cn_msg *msg; struct hv_kvp_msg *message; - int index = kvp_transaction.index; + struct hv_kvp_msg *in_msg; + __u8 operation = kvp_transaction.kvp_msg->kvp_hdr.operation; + __u8 pool = kvp_transaction.kvp_msg->kvp_hdr.pool; + __u32 val32; + __u64 val64; msg = kzalloc(sizeof(*msg) + sizeof(struct hv_kvp_msg) , GFP_ATOMIC); + if (!msg) + return; - if (msg) { - msg->id.idx = CN_KVP_IDX; - msg->id.val = CN_KVP_VAL; + msg->id.idx = CN_KVP_IDX; + msg->id.val = CN_KVP_VAL; - message = (struct hv_kvp_msg *)msg->data; - message->kvp_hdr.operation = KVP_OP_ENUMERATE; - message->body.kvp_enum_data.index = index; - msg->len = sizeof(struct hv_kvp_msg); - cn_netlink_send(msg, 0, GFP_ATOMIC); - kfree(msg); + message = (struct hv_kvp_msg *)msg->data; + message->kvp_hdr.operation = operation; + message->kvp_hdr.pool = pool; + in_msg = kvp_transaction.kvp_msg; + + /* + * The key/value strings sent from the host are encoded in + * in utf16; convert it to utf8 strings. + * The host assures us that the utf16 strings will not exceed + * the max lengths specified. We will however, reserve room + * for the string terminating character - in the utf16s_utf8s() + * function we limit the size of the buffer where the converted + * string is placed to HV_KVP_EXCHANGE_MAX_*_SIZE -1 to gaurantee + * that the strings can be properly terminated! + */ + + switch (message->kvp_hdr.operation) { + case KVP_OP_SET: + switch (in_msg->body.kvp_set.data.value_type) { + case REG_SZ: + /* + * The value is a string - utf16 encoding. + */ + message->body.kvp_set.data.value_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_set.data.value, + in_msg->body.kvp_set.data.value_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_set.data.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; + break; + + case REG_U32: + /* + * The value is a 32 bit scalar. + * We save this as a utf8 string. + */ + val32 = in_msg->body.kvp_set.data.value_u32; + message->body.kvp_set.data.value_size = + sprintf(message->body.kvp_set.data.value, + "%d", val32) + 1; + break; + + case REG_U64: + /* + * The value is a 64 bit scalar. + * We save this as a utf8 string. + */ + val64 = in_msg->body.kvp_set.data.value_u64; + message->body.kvp_set.data.value_size = + sprintf(message->body.kvp_set.data.value, + "%llu", val64) + 1; + break; + + } + case KVP_OP_GET: + message->body.kvp_set.data.key_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_set.data.key, + in_msg->body.kvp_set.data.key_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_set.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; + break; + + case KVP_OP_DELETE: + message->body.kvp_delete.key_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_delete.key, + in_msg->body.kvp_delete.key_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_delete.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; + break; + + case KVP_OP_ENUMERATE: + message->body.kvp_enum_data.index = + in_msg->body.kvp_enum_data.index; + break; } + + msg->len = sizeof(struct hv_kvp_msg); + cn_netlink_send(msg, 0, GFP_ATOMIC); + kfree(msg); + return; } @@ -159,10 +246,11 @@ static void kvp_respond_to_host(char *key, char *value, int error) { struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; + struct hv_kvp_exchg_msg_value *kvp_data; char *key_name; struct icmsg_hdr *icmsghdrp; - int keylen, valuelen; + int keylen = 0; + int valuelen = 0; u32 buf_len; struct vmbus_channel *channel; u64 req_id; @@ -189,6 +277,9 @@ kvp_respond_to_host(char *key, char *value, int error) kvp_transaction.active = false; + icmsghdrp = (struct icmsg_hdr *) + &recv_buffer[sizeof(struct vmbuspipe_hdr)]; + if (channel->onchannel_callback == NULL) /* * We have raced with util driver being unloaded; @@ -196,41 +287,66 @@ kvp_respond_to_host(char *key, char *value, int error) */ return; - icmsghdrp = (struct icmsg_hdr *) - &recv_buffer[sizeof(struct vmbuspipe_hdr)]; - kvp_msg = (struct hv_kvp_msg *) - &recv_buffer[sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->body.kvp_enum_data; - key_name = key; /* * If the error parameter is set, terminate the host's enumeration. */ if (error) { /* - * We don't support this index or the we have timedout; + * Something failed or the we have timedout; * terminate the host-side iteration by returning an error. */ icmsghdrp->status = HV_E_FAIL; goto response_done; } + icmsghdrp->status = HV_S_OK; + + kvp_msg = (struct hv_kvp_msg *) + &recv_buffer[sizeof(struct vmbuspipe_hdr) + + sizeof(struct icmsg_hdr)]; + + switch (kvp_transaction.kvp_msg->kvp_hdr.operation) { + case KVP_OP_GET: + kvp_data = &kvp_msg->body.kvp_get.data; + goto copy_value; + + case KVP_OP_SET: + case KVP_OP_DELETE: + goto response_done; + + default: + break; + } + + kvp_data = &kvp_msg->body.kvp_enum_data.data; + key_name = key; + /* * The windows host expects the key/value pair to be encoded - * in utf16. + * in utf16. Ensure that the key/value size reported to the host + * will be less than or equal to the MAX size (including the + * terminating character). */ keylen = utf8s_to_utf16s(key_name, strlen(key_name), UTF16_HOST_ENDIAN, - (wchar_t *) kvp_data->data.key, - HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2); - kvp_data->data.key_size = 2*(keylen + 1); /* utf16 encoding */ + (wchar_t *) kvp_data->key, + (HV_KVP_EXCHANGE_MAX_KEY_SIZE / 2) - 2); + kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */ + +copy_value: valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN, - (wchar_t *) kvp_data->data.value, - HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2); - kvp_data->data.value_size = 2*(valuelen + 1); /* utf16 encoding */ + (wchar_t *) kvp_data->value, + (HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2); + kvp_data->value_size = 2*(valuelen + 1); /* utf16 encoding */ - kvp_data->data.value_type = REG_SZ; /* all our values are strings */ - icmsghdrp->status = HV_S_OK; + /* + * If the utf8s to utf16s conversion failed; notify host + * of the error. + */ + if ((keylen < 0) || (valuelen < 0)) + icmsghdrp->status = HV_E_FAIL; + + kvp_data->value_type = REG_SZ; /* all our values are strings */ response_done: icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; @@ -257,11 +373,18 @@ void hv_kvp_onchannelcallback(void *context) u64 requestid; struct hv_kvp_msg *kvp_msg; - struct hv_kvp_msg_enumerate *kvp_data; struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; + if (kvp_transaction.active) { + /* + * We will defer processing this callback once + * the current transaction is complete. + */ + kvp_transaction.kvp_context = context; + return; + } vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid); @@ -276,29 +399,16 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - kvp_data = &kvp_msg->body.kvp_enum_data; - - /* - * We only support the "get" operation on - * "KVP_POOL_AUTO" pool. - */ - - if ((kvp_msg->kvp_hdr.pool != KVP_POOL_AUTO) || - (kvp_msg->kvp_hdr.operation != - KVP_OP_ENUMERATE)) { - icmsghdrp->status = HV_E_FAIL; - goto callback_done; - } - /* * Stash away this global state for completing the * transaction; note transactions are serialized. */ + kvp_transaction.recv_len = recvlen; kvp_transaction.recv_channel = channel; kvp_transaction.recv_req_id = requestid; kvp_transaction.active = true; - kvp_transaction.index = kvp_data->index; + kvp_transaction.kvp_msg = kvp_msg; /* * Get the information from the @@ -316,8 +426,6 @@ void hv_kvp_onchannelcallback(void *context) } -callback_done: - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; @@ -338,6 +446,14 @@ hv_kvp_init(struct hv_util_service *srv) return err; recv_buffer = srv->recv_buffer; + /* + * When this driver loads, the user level daemon that + * processes the host requests may not yet be running. + * Defer processing channel callbacks until the daemon + * has registered. + */ + kvp_transaction.active = true; + return 0; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a2d8c547f91b..e88a979107b5 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -119,6 +119,8 @@ */ #define REG_SZ 1 +#define REG_U32 4 +#define REG_U64 8 enum hv_kvp_exchg_op { KVP_OP_GET = 0, diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 00d3f7c099e0..a98878c874be 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -389,10 +389,16 @@ int main(void) } continue; + case KVP_OP_SET: + case KVP_OP_GET: + case KVP_OP_DELETE: default: break; } + if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) + goto kvp_done; + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; key_name = (char *)hv_msg->body.kvp_enum_data.data.key; key_value = (char *)hv_msg->body.kvp_enum_data.data.value; @@ -454,6 +460,7 @@ int main(void) * already in the receive buffer. Update the cn_msg header to * reflect the key value that has been added to the message */ +kvp_done: incoming_cn_msg->id.idx = CN_KVP_IDX; incoming_cn_msg->id.val = CN_KVP_VAL; -- cgit v1.2.3 From adc80ae60eae24a43a357bf5b30fb496f34aa605 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 16 Mar 2012 08:02:27 -0700 Subject: Tools: hv: Support enumeration from all the pools We have only supported enumeration only from the AUTO pool. Now support enumeration from all the available pools. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 7 +-- include/linux/hyperv.h | 1 + tools/hv/hv_kvp_daemon.c | 124 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index cfe60b02e3e8..6186025209ce 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -289,14 +289,15 @@ kvp_respond_to_host(char *key, char *value, int error) /* - * If the error parameter is set, terminate the host's enumeration. + * If the error parameter is set, terminate the host's enumeration + * on this pool. */ if (error) { /* * Something failed or the we have timedout; - * terminate the host-side iteration by returning an error. + * terminate the current host-side iteration. */ - icmsghdrp->status = HV_E_FAIL; + icmsghdrp->status = HV_S_CONT; goto response_done; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e88a979107b5..5852545e6bba 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -952,6 +952,7 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver); #define HV_S_OK 0x00000000 #define HV_E_FAIL 0x80004005 +#define HV_S_CONT 0x80070103 #define HV_ERROR_NOT_SUPPORTED 0x80070032 #define HV_ERROR_MACHINE_LOCKED 0x800704F7 diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 2fb9c3d09d7f..146fd6147e84 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -148,6 +148,51 @@ static void kvp_update_file(int pool) kvp_release_lock(pool); } +static void kvp_update_mem_state(int pool) +{ + FILE *filep; + size_t records_read = 0; + struct kvp_record *record = kvp_file_info[pool].records; + struct kvp_record *readp; + int num_blocks = kvp_file_info[pool].num_blocks; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + kvp_acquire_lock(pool); + + filep = fopen(kvp_file_info[pool].fname, "r"); + if (!filep) { + kvp_release_lock(pool); + syslog(LOG_ERR, "Failed to open file, pool: %d", pool); + exit(-1); + } + while (!feof(filep)) { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK * num_blocks, + filep); + + if (!feof(filep)) { + /* + * We have more data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * num_blocks); + + if (record == NULL) { + syslog(LOG_ERR, "malloc failed"); + exit(-1); + } + continue; + } + break; + } + + kvp_file_info[pool].num_blocks = num_blocks; + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records = records_read; + + kvp_release_lock(pool); +} static int kvp_file_init(void) { int ret, fd; @@ -223,8 +268,16 @@ static int kvp_key_delete(int pool, __u8 *key, int key_size) { int i; int j, k; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; + int num_records; + struct kvp_record *record; + + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) @@ -259,14 +312,23 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, { int i; int j, k; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; - int num_blocks = kvp_file_info[pool].num_blocks; + int num_records; + struct kvp_record *record; + int num_blocks; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + num_blocks = kvp_file_info[pool].num_blocks; + for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; @@ -304,13 +366,21 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, int value_size) { int i; - int num_records = kvp_file_info[pool].num_records; - struct kvp_record *record = kvp_file_info[pool].records; + int num_records; + struct kvp_record *record; if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) return 1; + /* + * First update the in-memory state. + */ + kvp_update_mem_state(pool); + + num_records = kvp_file_info[pool].num_records; + record = kvp_file_info[pool].records; + for (i = 0; i < num_records; i++) { if (memcmp(key, record[i].key, key_size)) continue; @@ -324,6 +394,31 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, return 1; } +static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, + __u8 *value, int value_size) +{ + struct kvp_record *record; + + /* + * First update our in-memory database. + */ + kvp_update_mem_state(pool); + record = kvp_file_info[pool].records; + + if (index >= kvp_file_info[pool].num_records) { + /* + * This is an invalid index; terminate enumeration; + * - a NULL value will do the trick. + */ + strcpy(value, ""); + return; + } + + memcpy(key, record[index].key, key_size); + memcpy(value, record[index].value, value_size); +} + + void kvp_get_os_info(void) { FILE *file; @@ -678,6 +773,21 @@ int main(void) if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) goto kvp_done; + /* + * If the pool is KVP_POOL_AUTO, dynamically generate + * both the key and the value; if not read from the + * appropriate pool. + */ + if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { + kvp_pool_enumerate(hv_msg->kvp_hdr.pool, + hv_msg->body.kvp_enum_data.index, + hv_msg->body.kvp_enum_data.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE, + hv_msg->body.kvp_enum_data.data.value, + HV_KVP_EXCHANGE_MAX_VALUE_SIZE); + goto kvp_done; + } + hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; key_name = (char *)hv_msg->body.kvp_enum_data.data.key; key_value = (char *)hv_msg->body.kvp_enum_data.data.value; -- cgit v1.2.3