From 4e4d6d860b9393c5395ba5920edb5b4c5d43a3a3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 18 Dec 2011 20:05:43 -0800 Subject: sysfs: Add s_hash to sysfs_dirent and order directory entries by hash Compute a 31 bit hash of directory entries (that can fit in a signed 32bit off_t) and index the sysfs directory entries by that hash, replacing the per directory indexes by name and by inode. Because we now only use a single rbtree this reduces the size of sysfs_dirent by 2 pointers. Because we have fewer cases to deal with the code is now simpler. For now I use the simple hash that the dcache uses as that is easy to use and seems simple enough. In addition to makeing the code simpler using a hash for the file position in readdir brings sysfs in line with other filesystems that have non-trivial directory structures. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 219 +++++++++++++++++++++++++++++-------------------------- fs/sysfs/sysfs.h | 9 +-- 2 files changed, 120 insertions(+), 108 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 7fdf6a7..0daf255 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -22,76 +22,103 @@ #include #include #include +#include #include "sysfs.h" DEFINE_MUTEX(sysfs_mutex); DEFINE_SPINLOCK(sysfs_assoc_lock); +#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb); + static DEFINE_SPINLOCK(sysfs_ino_lock); static DEFINE_IDA(sysfs_ino_ida); /** - * sysfs_link_sibling - link sysfs_dirent into sibling list + * sysfs_name_hash + * @ns: Namespace tag to hash + * @name: Null terminated string to hash + * + * Returns 31 bit hash of ns + name (so it fits in an off_t ) + */ +static unsigned int sysfs_name_hash(const void *ns, const char *name) +{ + unsigned long hash = init_name_hash(); + unsigned int len = strlen(name); + while (len--) + hash = partial_name_hash(*name++, hash); + hash = ( end_name_hash(hash) ^ hash_ptr( (void *)ns, 31 ) ); + hash &= 0x7fffffffU; + /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ + if (hash < 1) + hash += 2; + if (hash >= INT_MAX) + hash = INT_MAX - 1; + return hash; +} + +static int sysfs_name_compare(unsigned int hash, const void *ns, + const char *name, const struct sysfs_dirent *sd) +{ + if (hash != sd->s_hash) + return hash - sd->s_hash; + if (ns != sd->s_ns) + return ns - sd->s_ns; + return strcmp(name, sd->s_name); +} + +static int sysfs_sd_compare(const struct sysfs_dirent *left, + const struct sysfs_dirent *right) +{ + return sysfs_name_compare(left->s_hash, left->s_ns, left->s_name, + right); +} + +/** + * sysfs_link_subling - link sysfs_dirent into sibling rbtree * @sd: sysfs_dirent of interest * - * Link @sd into its sibling list which starts from + * Link @sd into its sibling rbtree which starts from * sd->s_parent->s_dir.children. * * Locking: * mutex_lock(sysfs_mutex) + * + * RETURNS: + * 0 on susccess -EEXIST on failure. */ -static void sysfs_link_sibling(struct sysfs_dirent *sd) +static int sysfs_link_sibling(struct sysfs_dirent *sd) { - struct sysfs_dirent *parent_sd = sd->s_parent; - - struct rb_node **p; - struct rb_node *parent; + struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; + struct rb_node *parent = NULL; if (sysfs_type(sd) == SYSFS_DIR) - parent_sd->s_dir.subdirs++; - - p = &parent_sd->s_dir.inode_tree.rb_node; - parent = NULL; - while (*p) { - parent = *p; -#define node rb_entry(parent, struct sysfs_dirent, inode_node) - if (sd->s_ino < node->s_ino) { - p = &node->inode_node.rb_left; - } else if (sd->s_ino > node->s_ino) { - p = &node->inode_node.rb_right; - } else { - printk(KERN_CRIT "sysfs: inserting duplicate inode '%lx'\n", - (unsigned long) sd->s_ino); - BUG(); - } -#undef node - } - rb_link_node(&sd->inode_node, parent, p); - rb_insert_color(&sd->inode_node, &parent_sd->s_dir.inode_tree); - - p = &parent_sd->s_dir.name_tree.rb_node; - parent = NULL; - while (*p) { - int c; - parent = *p; -#define node rb_entry(parent, struct sysfs_dirent, name_node) - c = strcmp(sd->s_name, node->s_name); - if (c < 0) { - p = &node->name_node.rb_left; - } else { - p = &node->name_node.rb_right; - } -#undef node + sd->s_parent->s_dir.subdirs++; + + while (*node) { + struct sysfs_dirent *pos; + int result; + + pos = to_sysfs_dirent(*node); + parent = *node; + result = sysfs_sd_compare(sd, pos); + if (result < 0) + node = &pos->s_rb.rb_left; + else if (result > 0) + node = &pos->s_rb.rb_right; + else + return -EEXIST; } - rb_link_node(&sd->name_node, parent, p); - rb_insert_color(&sd->name_node, &parent_sd->s_dir.name_tree); + /* add new node and rebalance the tree */ + rb_link_node(&sd->s_rb, parent, node); + rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); + return 0; } /** - * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list + * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree * @sd: sysfs_dirent of interest * - * Unlink @sd from its sibling list which starts from + * Unlink @sd from its sibling rbtree which starts from * sd->s_parent->s_dir.children. * * Locking: @@ -102,8 +129,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) if (sysfs_type(sd) == SYSFS_DIR) sd->s_parent->s_dir.subdirs--; - rb_erase(&sd->inode_node, &sd->s_parent->s_dir.inode_tree); - rb_erase(&sd->name_node, &sd->s_parent->s_dir.name_tree); + rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); } /** @@ -402,6 +428,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { struct sysfs_inode_attrs *ps_iattr; + int ret; if (!!sysfs_ns_type(acxt->parent_sd) != !!sd->s_ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", @@ -410,12 +437,12 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) return -EINVAL; } - if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) - return -EEXIST; - + sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); sd->s_parent = sysfs_get(acxt->parent_sd); - sysfs_link_sibling(sd); + ret = sysfs_link_sibling(sd); + if (ret) + return ret; /* Update timestamps on the parent */ ps_iattr = acxt->parent_sd->s_iattr; @@ -565,8 +592,8 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, const void *ns, const unsigned char *name) { - struct rb_node *p = parent_sd->s_dir.name_tree.rb_node; - struct sysfs_dirent *found = NULL; + struct rb_node *node = parent_sd->s_dir.children.rb_node; + unsigned int hash; if (!!sysfs_ns_type(parent_sd) != !!ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", @@ -575,33 +602,21 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, return NULL; } - while (p) { - int c; -#define node rb_entry(p, struct sysfs_dirent, name_node) - c = strcmp(name, node->s_name); - if (c < 0) { - p = node->name_node.rb_left; - } else if (c > 0) { - p = node->name_node.rb_right; - } else { - found = node; - p = node->name_node.rb_left; - } -#undef node - } - - if (found) { - while (found->s_ns != ns) { - p = rb_next(&found->name_node); - if (!p) - return NULL; - found = rb_entry(p, struct sysfs_dirent, name_node); - if (strcmp(name, found->s_name)) - return NULL; - } + hash = sysfs_name_hash(ns, name); + while (node) { + struct sysfs_dirent *sd; + int result; + + sd = to_sysfs_dirent(node); + result = sysfs_name_compare(hash, ns, name, sd); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return sd; } - - return found; + return NULL; } /** @@ -804,9 +819,9 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); sysfs_addrm_start(&acxt, dir_sd); - pos = rb_first(&dir_sd->s_dir.inode_tree); + pos = rb_first(&dir_sd->s_dir.children); while (pos) { - struct sysfs_dirent *sd = rb_entry(pos, struct sysfs_dirent, inode_node); + struct sysfs_dirent *sd = to_sysfs_dirent(pos); pos = rb_next(pos); if (sysfs_type(sd) != SYSFS_DIR) sysfs_remove_one(&acxt, sd); @@ -919,38 +934,36 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp) } static struct sysfs_dirent *sysfs_dir_pos(const void *ns, - struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) + struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) { if (pos) { int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && pos->s_parent == parent_sd && - ino == pos->s_ino; + hash == pos->s_hash; sysfs_put(pos); if (!valid) pos = NULL; } - if (!pos && (ino > 1) && (ino < INT_MAX)) { - struct rb_node *p = parent_sd->s_dir.inode_tree.rb_node; - while (p) { -#define node rb_entry(p, struct sysfs_dirent, inode_node) - if (ino < node->s_ino) { - pos = node; - p = node->inode_node.rb_left; - } else if (ino > node->s_ino) { - p = node->inode_node.rb_right; - } else { - pos = node; + if (!pos && (hash > 1) && (hash < INT_MAX)) { + struct rb_node *node = parent_sd->s_dir.children.rb_node; + while (node) { + pos = to_sysfs_dirent(node); + + if (hash < pos->s_hash) + node = node->rb_left; + else if (hash > pos->s_hash) + node = node->rb_right; + else break; - } -#undef node } } + /* Skip over entries in the wrong namespace */ while (pos && pos->s_ns != ns) { - struct rb_node *p = rb_next(&pos->inode_node); - if (!p) + struct rb_node *node = rb_next(&pos->s_rb); + if (!node) pos = NULL; else - pos = rb_entry(p, struct sysfs_dirent, inode_node); + pos = to_sysfs_dirent(node); } return pos; } @@ -960,11 +973,11 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, { pos = sysfs_dir_pos(ns, parent_sd, ino, pos); if (pos) do { - struct rb_node *p = rb_next(&pos->inode_node); - if (!p) + struct rb_node *node = rb_next(&pos->s_rb); + if (!node) pos = NULL; else - pos = rb_entry(p, struct sysfs_dirent, inode_node); + pos = to_sysfs_dirent(node); } while (pos && pos->s_ns != ns); return pos; } @@ -1006,7 +1019,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) len = strlen(name); ino = pos->s_ino; type = dt_type(pos); - filp->f_pos = ino; + filp->f_pos = pos->s_hash; filp->private_data = sysfs_get(pos); mutex_unlock(&sysfs_mutex); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 7484a36..2b5c923 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -20,9 +20,8 @@ struct sysfs_elem_dir { struct kobject *kobj; unsigned long subdirs; - - struct rb_root inode_tree; - struct rb_root name_tree; + /* children rbtree starts here and goes through sd->s_rb */ + struct rb_root children; }; struct sysfs_elem_symlink { @@ -62,8 +61,7 @@ struct sysfs_dirent { struct sysfs_dirent *s_parent; const char *s_name; - struct rb_node inode_node; - struct rb_node name_node; + struct rb_node s_rb; union { struct completion *completion; @@ -71,6 +69,7 @@ struct sysfs_dirent { } u; const void *s_ns; /* namespace tag */ + unsigned int s_hash; /* ns + name hash */ union { struct sysfs_elem_dir s_dir; struct sysfs_elem_symlink s_symlink; -- cgit v1.1 From 15a3382451e51925facfe430deeca63d90137f5d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 18 Dec 2011 20:07:23 -0800 Subject: sysfs: Reduce s_flags to an unsinged short so it packs well with s_mode On 32bit this reduces sizeof(struct sysfs_dirent) by 2 bytes. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/sysfs.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 2b5c923..1999494 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -77,7 +77,7 @@ struct sysfs_dirent { struct sysfs_elem_bin_attr s_bin_attr; }; - unsigned int s_flags; + unsigned short s_flags; umode_t s_mode; ino_t s_ino; struct sysfs_inode_attrs *s_iattr; @@ -94,11 +94,11 @@ struct sysfs_dirent { #define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) /* identify any namespace tag on sysfs_dirents */ -#define SYSFS_NS_TYPE_MASK 0xff00 +#define SYSFS_NS_TYPE_MASK 0xf00 #define SYSFS_NS_TYPE_SHIFT 8 #define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) -#define SYSFS_FLAG_REMOVED 0x020000 +#define SYSFS_FLAG_REMOVED 0x02000 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { -- cgit v1.1 From cafa6b5dd7ce4f0e0a30be301be4efed587a7808 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 18 Dec 2011 20:08:16 -0800 Subject: sysfs: Store the sysfs inode in an unsigned int. Store the sysfs inode number in an unsided int because ida inode allocator can return at most a 31 bit number, reducing the size of struct sysfs_dirent by 8 bytes on 64bit platforms. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 4 ++-- fs/sysfs/sysfs.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 0daf255..0589c9a 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -224,7 +224,7 @@ static void sysfs_deactivate(struct sysfs_dirent *sd) rwsem_release(&sd->dep_map, 1, _RET_IP_); } -static int sysfs_alloc_ino(ino_t *pino) +static int sysfs_alloc_ino(unsigned int *pino) { int ino, rc; @@ -243,7 +243,7 @@ static int sysfs_alloc_ino(ino_t *pino) return rc; } -static void sysfs_free_ino(ino_t ino) +static void sysfs_free_ino(unsigned int ino) { spin_lock(&sysfs_ino_lock); ida_remove(&sysfs_ino_ida, ino); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 1999494..661a963 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -79,7 +79,7 @@ struct sysfs_dirent { unsigned short s_flags; umode_t s_mode; - ino_t s_ino; + unsigned int s_ino; struct sysfs_inode_attrs *s_iattr; }; -- cgit v1.1 From 524b6c5b39b931311dfe5a2f5abae2f5c9731676 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 18 Dec 2011 20:09:31 -0800 Subject: sysfs: Kill nlink counting. Tracking the number of subdirectories requires an extra field that increases the size of sysfs_dirent. nlinks are not particularly interesting for sysfs and the nlink counts are wrong when network namespaces are involved so stop counting them, and always return nlink == 1. Userspace already knows that directories with nlink == 1 have an nlink count they can't use to count subdirectories. This reduces the size of sysfs_dirent by 8 bytes on 64bit platforms. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 6 ------ fs/sysfs/inode.c | 3 --- fs/sysfs/sysfs.h | 1 - 3 files changed, 10 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 0589c9a..ea64d01 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -91,9 +91,6 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd) struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; struct rb_node *parent = NULL; - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs++; - while (*node) { struct sysfs_dirent *pos; int result; @@ -126,9 +123,6 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd) */ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) { - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs--; - rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 4a802b4..0ac3e1c 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -216,9 +216,6 @@ static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) iattrs->ia_secdata, iattrs->ia_secdata_len); } - - if (sysfs_type(sd) == SYSFS_DIR) - set_nlink(inode, sd->s_dir.subdirs + 2); } int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 661a963..6289a00 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -19,7 +19,6 @@ struct sysfs_open_dirent; struct sysfs_elem_dir { struct kobject *kobj; - unsigned long subdirs; /* children rbtree starts here and goes through sd->s_rb */ struct rb_root children; }; -- cgit v1.1 From 07100be7e0495ff39237d48886bca7396c873db7 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:11:09 -0500 Subject: dynamic_debug: fix whitespace complaints from scripts/cleanfile Style cleanups. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index dcdade3..e487d13 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -187,7 +187,7 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords) if (!*buf) break; /* oh, it was trailing whitespace */ - /* Run `end' over a word, either whitespace separated or quoted */ + /* find `end' of word, whitespace separated or quoted */ if (*buf == '"' || *buf == '\'') { int quote = *buf++; for (end = buf ; *end && *end != quote ; end++) @@ -199,8 +199,8 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords) ; BUG_ON(end == buf); } - /* Here `buf' is the start of the word, `end' is one past the end */ + /* `buf' is start of word, `end' is one past its end */ if (nwords == maxwords) return -EINVAL; /* ran out of words[] before bytes */ if (*end) @@ -452,7 +452,8 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf) pos += snprintf(buf + pos, remaining(pos), "%s:", desc->function); if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO) - pos += snprintf(buf + pos, remaining(pos), "%d:", desc->lineno); + pos += snprintf(buf + pos, remaining(pos), "%d:", + desc->lineno); if (pos - pos_after_tid) pos += snprintf(buf + pos, remaining(pos), " "); if (pos >= PREFIX_SIZE) @@ -708,10 +709,11 @@ static const struct seq_operations ddebug_proc_seqops = { }; /* - * File_ops->open method for /dynamic_debug/control. Does the seq_file - * setup dance, and also creates an iterator to walk the _ddebugs. - * Note that we create a seq_file always, even for O_WRONLY files - * where it's not needed, as doing so simplifies the ->release method. + * File_ops->open method for /dynamic_debug/control. Does + * the seq_file setup dance, and also creates an iterator to walk the + * _ddebugs. Note that we create a seq_file always, even for O_WRONLY + * files where it's not needed, as doing so simplifies the ->release + * method. */ static int ddebug_proc_open(struct inode *inode, struct file *file) { -- cgit v1.1 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 ++++------ lib/dynamic_debug.c | 4 ---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 0564e3c..f71a6b04 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) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index e487d13..416c079 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -151,10 +151,6 @@ static void ddebug_change(const struct ddebug_query *query, if (newflags == dp->flags) continue; dp->flags = newflags; - if (newflags) - dp->enabled = 1; - else - dp->enabled = 0; if (verbose) pr_info("changed %s:%d [%s]%s %s\n", dp->filename, dp->lineno, -- cgit v1.1 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(-) diff --git a/include/linux/device.h b/include/linux/device.h index 5b3adb8..a782d7f 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 f71a6b04..29ea09a 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 0eac07c..f486f63 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 f0e22f7..f9abd93 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.1 From 74df138d508eb35e8b929e165e5403cfbb46a0c5 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:24 -0500 Subject: dynamic_debug: change verbosity at runtime Allow changing dynamic_debug verbosity at run-time, to ease debugging of ddebug queries as you add them, improving usability. at boot time: dynamic_debug.verbose=1 at runtime: root@voyage:~# echo 1 > /sys/module/dynamic_debug/parameters/verbose Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 416c079..8c88b89 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -60,6 +60,7 @@ struct ddebug_iter { static DEFINE_MUTEX(ddebug_lock); static LIST_HEAD(ddebug_tables); static int verbose = 0; +module_param(verbose, int, 0644); /* Return the last part of a pathname */ static inline const char *basename(const char *path) -- cgit v1.1 From bc757f6f5bf4e9251bbc1a3419c94ffe9fd3e2ee Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:29 -0500 Subject: dynamic_debug: replace strcpy with strlcpy, in ddebug_setup_query() Replace strcpy with strlcpy, and add define for the size constant. [jbaron@redhat.com: Use DDEBUG_STRING_SIZE for overflow check] Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 8c88b89..6fc8622 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -525,14 +525,16 @@ EXPORT_SYMBOL(__dynamic_netdev_dbg); #endif -static __initdata char ddebug_setup_string[1024]; +#define DDEBUG_STRING_SIZE 1024 +static __initdata char ddebug_setup_string[DDEBUG_STRING_SIZE]; + static __init int ddebug_setup_query(char *str) { - if (strlen(str) >= 1024) { + if (strlen(str) >= DDEBUG_STRING_SIZE) { pr_warn("ddebug boot param string too large\n"); return 0; } - strcpy(ddebug_setup_string, str); + strlcpy(ddebug_setup_string, str, DDEBUG_STRING_SIZE); return 1; } -- cgit v1.1 From ae27f86a21eb9a9e005f06b126eb88662ba4f940 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:34 -0500 Subject: dynamic_debug: pr_err() call should not depend upon verbosity Issue keyword/parsing errors even w/o verbose set; uncover otherwize mysterious non-functionality. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 6fc8622..d232025 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -322,8 +322,7 @@ static int ddebug_parse_query(char *words[], int nwords, query->last_lineno = query->first_lineno; } } else { - if (verbose) - pr_err("unknown keyword \"%s\"\n", words[i]); + pr_err("unknown keyword \"%s\"\n", words[i]); return -EINVAL; } } -- cgit v1.1 From d6a238d25014d0ff918410d73e2a6300bca5d1f1 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:39 -0500 Subject: dynamic_debug: drop explicit !=NULL checks Convert 'if (x !=NULL)' checks into 'if (x)'. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index d232025..b199e09 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -115,27 +115,26 @@ static void ddebug_change(const struct ddebug_query *query, list_for_each_entry(dt, &ddebug_tables, link) { /* match against the module name */ - if (query->module != NULL && - strcmp(query->module, dt->mod_name)) + if (query->module && strcmp(query->module, dt->mod_name)) continue; for (i = 0 ; i < dt->num_ddebugs ; i++) { struct _ddebug *dp = &dt->ddebugs[i]; /* match against the source filename */ - if (query->filename != NULL && + if (query->filename && strcmp(query->filename, dp->filename) && strcmp(query->filename, basename(dp->filename))) continue; /* match against the function */ - if (query->function != NULL && + if (query->function && strcmp(query->function, dp->function)) continue; /* match against the format */ - if (query->format != NULL && - strstr(dp->format, query->format) == NULL) + if (query->format && + !strstr(dp->format, query->format)) continue; /* match against the line number range */ -- cgit v1.1 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(-) diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index 29ea09a..fc39640 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 b199e09..cde4dfe 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.1 From 820874c75ea0d3a9c22d69d6eaad42a279d6756c Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:49 -0500 Subject: dynamic_debug: tighten up error checking on debug queries Issue error when a match-spec is given multiple times in a rule. Previous code kept last one, but was silent about it. Docs imply only one is allowed by saying match-specs are ANDed together, given that module M cannot match both A and B. Also error when last_line < 1st_line. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index cde4dfe..5a7bacc 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -276,6 +276,19 @@ static char *unescape(char *str) return str; } +static int check_set(const char **dest, char *src, char *name) +{ + int rc = 0; + + if (*dest) { + rc = -EINVAL; + pr_err("match-spec:%s val:%s overridden by %s", + name, *dest, src); + } + *dest = src; + return rc; +} + /* * Parse words[] as a ddebug query specification, which is a series * of (keyword, value) pairs chosen from these possibilities: @@ -287,11 +300,15 @@ static char *unescape(char *str) * format * line * line - // where either may be empty + * + * Only 1 of each type is allowed. + * Returns 0 on success, <0 on error. */ static int ddebug_parse_query(char *words[], int nwords, struct ddebug_query *query) { unsigned int i; + int rc; /* check we have an even number of words */ if (nwords % 2 != 0) @@ -300,24 +317,32 @@ static int ddebug_parse_query(char *words[], int nwords, for (i = 0 ; i < nwords ; i += 2) { if (!strcmp(words[i], "func")) - query->function = words[i+1]; + rc = check_set(&query->function, words[i+1], "func"); else if (!strcmp(words[i], "file")) - query->filename = words[i+1]; + rc = check_set(&query->filename, words[i+1], "file"); else if (!strcmp(words[i], "module")) - query->module = words[i+1]; + rc = check_set(&query->module, words[i+1], "module"); else if (!strcmp(words[i], "format")) - query->format = unescape(words[i+1]); + rc = check_set(&query->format, unescape(words[i+1]), + "format"); else if (!strcmp(words[i], "line")) { char *first = words[i+1]; char *last = strchr(first, '-'); + if (query->first_lineno || query->last_lineno) { + pr_err("match-spec:line given 2 times\n"); + return -EINVAL; + } if (last) *last++ = '\0'; if (parse_lineno(first, &query->first_lineno) < 0) return -EINVAL; - if (last != NULL) { + if (last) { /* range - */ - if (parse_lineno(last, &query->last_lineno) < 0) + if (parse_lineno(last, &query->last_lineno) + < query->first_lineno) { + pr_err("last-line < 1st-line\n"); return -EINVAL; + } } else { query->last_lineno = query->first_lineno; } @@ -325,6 +350,8 @@ static int ddebug_parse_query(char *words[], int nwords, pr_err("unknown keyword \"%s\"\n", words[i]); return -EINVAL; } + if (rc) + return rc; } if (verbose) -- cgit v1.1 From b5b78f83854af15e04c63fdbc6efed9355afbe8f Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:12:54 -0500 Subject: dynamic_debug: early return if _ddebug table is empty If _ddebug table is empty (in a CONFIG_DYNAMIC_DEBUG build this shouldn't happen), then warn (error?) and return early. This skips empty table scan and parsing of setup-string, including the pr_info call noting the parse. By inspection, copy return-code handling from 1st ddebug_add_module() callsite to 2nd. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 5a7bacc..4be55d8 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -871,23 +871,28 @@ static int __init dynamic_debug_init(void) int ret = 0; int n = 0; - if (__start___verbose != __stop___verbose) { - iter = __start___verbose; - modname = iter->modname; - iter_start = iter; - for (; iter < __stop___verbose; iter++) { - if (strcmp(modname, iter->modname)) { - ret = ddebug_add_module(iter_start, n, modname); - if (ret) - goto out_free; - n = 0; - modname = iter->modname; - iter_start = iter; - } - n++; + if (__start___verbose == __stop___verbose) { + pr_warn("_ddebug table is empty in a " + "CONFIG_DYNAMIC_DEBUG build"); + return 1; + } + iter = __start___verbose; + modname = iter->modname; + iter_start = iter; + for (; iter < __stop___verbose; iter++) { + if (strcmp(modname, iter->modname)) { + ret = ddebug_add_module(iter_start, n, modname); + if (ret) + goto out_free; + n = 0; + modname = iter->modname; + iter_start = iter; } - ret = ddebug_add_module(iter_start, n, modname); + n++; } + ret = ddebug_add_module(iter_start, n, modname); + if (ret) + goto out_free; /* ddebug_query boot param got passed -> set it up */ if (ddebug_setup_string[0] != '\0') { -- cgit v1.1 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(-) diff --git a/include/linux/dynamic_debug.h b/include/linux/dynamic_debug.h index fc39640..7e3c53a 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.1 From 8bd6026e88cb2eb1e60ee40c7a1a0786b2db6623 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:13:03 -0500 Subject: dynamic_debug: chop off comments in ddebug_tokenize If a token begins with #, the remainder of query string is a comment, so drop it. Doing it here avoids '#' in quoted strings. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 4be55d8..86a9abb 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -183,6 +183,8 @@ static int ddebug_tokenize(char *buf, char *words[], int maxwords) buf = skip_spaces(buf); if (!*buf) break; /* oh, it was trailing whitespace */ + if (*buf == '#') + break; /* token starts comment, skip rest of line */ /* find `end' of word, whitespace separated or quoted */ if (*buf == '"' || *buf == '\'') { -- cgit v1.1 From 7281491c594e7b8501eb5dfcf6cd3724f8a1b5b0 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:13:07 -0500 Subject: dynamic_debug: enlarge command/query write buffer Current query write buffer is 256 bytes, on stack. In comparison, the ddebug_query boot-arg is 1024. Allocate the buffer off heap, and enlarge it to 4096 bytes, big enough for ~100 queries (at 40 bytes each), and error out if not. This makes it play nicely with large query sets (to be added later). The buffer should be enough for most uses, and others should probably be split into subsets. [jbaron@redhat.com: changed USER_BUF_PAGE from 4095 -> 4096 ] Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 86a9abb..d8773dc 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -570,24 +570,32 @@ __setup("ddebug_query=", ddebug_setup_query); * File_ops->write method for /dynamic_debug/conrol. Gathers the * command text from userspace, parses and executes it. */ +#define USER_BUF_PAGE 4096 static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { - char tmpbuf[256]; + char *tmpbuf; int ret; if (len == 0) return 0; - /* we don't check *offp -- multiple writes() are allowed */ - if (len > sizeof(tmpbuf)-1) + if (len > USER_BUF_PAGE - 1) { + pr_warn("expected <%d bytes into control\n", USER_BUF_PAGE); return -E2BIG; - if (copy_from_user(tmpbuf, ubuf, len)) + } + tmpbuf = kmalloc(len + 1, GFP_KERNEL); + if (!tmpbuf) + return -ENOMEM; + if (copy_from_user(tmpbuf, ubuf, len)) { + kfree(tmpbuf); return -EFAULT; + } tmpbuf[len] = '\0'; if (verbose) pr_info("read %d bytes from userspace\n", (int)len); ret = ddebug_exec_query(tmpbuf); + kfree(tmpbuf); if (ret) return ret; -- cgit v1.1 From 2b6783191da7211c88f98eb1a2bd2027bff36e30 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:13:12 -0500 Subject: dynamic_debug: add trim_prefix() to provide source-root relative paths trim_prefix(path) skips past the absolute source path root, and returns the pointer to the relative path from there. It is used to shorten the displayed path in $DBGMT/dynamic_debug/control via ddebug_proc_show(), and in ddebug_change() to allow relative filenames to be used in applied queries. For example: ~# echo file kernel/freezer.c +p > $DBGMT/dynamic_debug/control kernel/freezer.c:128 [freezer]cancel_freezing p " clean up: %s\012" trim_prefix(path) insures common prefix before trimming it, so out-of-tree module paths are shown as full absolute paths. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 7 ++++--- lib/dynamic_debug.c | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index f959909..378b5d1 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -144,11 +144,12 @@ func func svc_tcp_accept file - The given string is compared against either the full - pathname or the basename of the source file of each - callsite. Examples: + The given string is compared against either the full pathname, the + src-root relative pathname, or the basename of the source file of + each callsite. Examples: file svcsock.c + file kernel/freezer.c file /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c module diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index d8773dc..a5508a1 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -69,6 +69,17 @@ static inline const char *basename(const char *path) return tail ? tail+1 : path; } +/* Return the path relative to source root */ +static inline const char *trim_prefix(const char *path) +{ + int skip = strlen(__FILE__) - strlen("lib/dynamic_debug.c"); + + if (strncmp(path, __FILE__, skip)) + skip = 0; /* prefix mismatch, don't skip */ + + return path + skip; +} + static struct { unsigned flag:8; char opt_char; } opt_array[] = { { _DPRINTK_FLAGS_PRINT, 'p' }, { _DPRINTK_FLAGS_INCL_MODNAME, 'm' }, @@ -125,7 +136,8 @@ static void ddebug_change(const struct ddebug_query *query, /* match against the source filename */ if (query->filename && strcmp(query->filename, dp->filename) && - strcmp(query->filename, basename(dp->filename))) + strcmp(query->filename, basename(dp->filename)) && + strcmp(query->filename, trim_prefix(dp->filename))) continue; /* match against the function */ @@ -154,7 +166,7 @@ static void ddebug_change(const struct ddebug_query *query, dp->flags = newflags; if (verbose) pr_info("changed %s:%d [%s]%s =%s\n", - dp->filename, dp->lineno, + trim_prefix(dp->filename), dp->lineno, dt->mod_name, dp->function, ddebug_describe_flags(dp, flagbuf, sizeof(flagbuf))); @@ -714,7 +726,7 @@ static int ddebug_proc_show(struct seq_file *m, void *p) } seq_printf(m, "%s:%u [%s]%s =%s \"", - dp->filename, dp->lineno, + trim_prefix(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\""); -- cgit v1.1 From 574b3725e327531c70361d1a10b8dc8dd2b93590 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:13:16 -0500 Subject: dynamic_debug: factor vpr_info_dq out of ddebug_parse_query Factor pr_info(query) out of ddebug_parse_query, into vpr_info_dq(), for reuse later. Also change the printed labels: file, func to agree with the query-spec keywords accepted in the control file. Pass "" when string is null, to avoid "(null)" output from sprintf. For format print, use precision to skip last char, assuming its '\n', no great harm if not, its a debug msg. Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- lib/dynamic_debug.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index a5508a1..93fc5d5 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -107,6 +107,22 @@ static char *ddebug_describe_flags(struct _ddebug *dp, char *buf, return buf; } +#define vpr_info_dq(q, msg) \ +do { \ + if (verbose) \ + /* trim last char off format print */ \ + pr_info("%s: func=\"%s\" file=\"%s\" " \ + "module=\"%s\" format=\"%.*s\" " \ + "lineno=%u-%u", \ + msg, \ + q->function ? q->function : "", \ + q->filename ? q->filename : "", \ + q->module ? q->module : "", \ + (int)(q->format ? strlen(q->format) - 1 : 0), \ + q->format ? q->format : "", \ + q->first_lineno, q->last_lineno); \ +} while (0) + /* * Search the tables for _ddebug's which match the given * `query' and apply the `flags' and `mask' to them. Tells @@ -367,14 +383,7 @@ static int ddebug_parse_query(char *words[], int nwords, if (rc) return rc; } - - if (verbose) - pr_info("q->function=\"%s\" q->filename=\"%s\" " - "q->module=\"%s\" q->format=\"%s\" q->lineno=%u-%u\n", - query->function, query->filename, - query->module, query->format, query->first_lineno, - query->last_lineno); - + vpr_info_dq(query, "parsed"); return 0; } -- cgit v1.1 From 85f7f6c0edb8414053d788229c97d5ecff21efab Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Mon, 19 Dec 2011 17:13:21 -0500 Subject: dynamic_debug: process multiple debug-queries on a line Insert ddebug_exec_queries() in place of ddebug_exec_query(). It splits the query string on [;\n], and calls ddebug_exec_query() on each. All queries are processed independent of errors, allowing a query to fail, for example when a module is not installed. Empty lines and comments are skipped. Errors are counted, and the last error seen (negative) or the number of callsites found (0 or positive) is returned. Return code checks are altered accordingly. With this, multiple queries can be given in ddebug_query, allowing more selective enabling of callsites. As a side effect, a set of commands can be batched in: cat cmd-file > $DBGMT/dynamic_debug/control We dont want a ddebug_query syntax error to kill the dynamic debug facility, so dynamic_debug_init() zeros ddebug_exec_queries()'s return code after logging the appropriate message, so that ddebug tables are preserved and $DBGMT/dynamic_debug/control file is created. This would be appropriate even without accepting multiple queries. This patch also alters ddebug_change() to return number of callsites matched (which typically is the same as number of callsites changed). ddebug_exec_query() also returns the number found, or a negative value if theres a parse error on the query. Splitting on [;\n] prevents their use in format-specs, but selecting callsites on punctuation is brittle anyway, meaningful and selective substrings are more typical. Note: splitting queries on ';' before handling trailing #comments means that a ';' also terminates a comment, and text after the ';' is treated as another query. This trailing query will almost certainly result in a parse error and thus have no effect other than the error message. The double corner case with unexpected results is: ddebug_query="func foo +p # enable foo ; +p" Signed-off-by: Jim Cromie Signed-off-by: Jason Baron Signed-off-by: Greg Kroah-Hartman --- Documentation/dynamic-debug-howto.txt | 23 ++++------- lib/dynamic_debug.c | 73 ++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 30 deletions(-) diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 378b5d1..74e6c77 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -12,7 +12,7 @@ dynamically enabled per-callsite. Dynamic debug has even more useful features: * Simple query language allows turning on and off debugging statements by - matching any combination of: + matching any combination of 0 or 1 of: - source filename - function name @@ -79,31 +79,24 @@ Command Language Reference ========================== At the lexical level, a command comprises a sequence of words separated -by whitespace characters. Note that newlines are treated as word -separators and do *not* end a command or allow multiple commands to -be done together. So these are all equivalent: +by spaces or tabs. So these are all equivalent: nullarbor:~ # echo -c 'file svcsock.c line 1603 +p' > /dynamic_debug/control nullarbor:~ # echo -c ' file svcsock.c line 1603 +p ' > /dynamic_debug/control -nullarbor:~ # echo -c 'file svcsock.c\nline 1603 +p' > - /dynamic_debug/control nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > /dynamic_debug/control -Commands are bounded by a write() system call. If you want to do -multiple commands you need to do a separate "echo" for each, like: +Command submissions are bounded by a write() system call. +Multiple commands can be written together, separated by ';' or '\n'. -nullarbor:~ # echo 'file svcsock.c line 1603 +p' > /proc/dprintk ;\ -> echo 'file svcsock.c line 1563 +p' > /proc/dprintk + ~# echo "func pnpacpi_get_resources +p; func pnp_assign_mem +p" \ + > /dynamic_debug/control -or even like: +If your query set is big, you can batch them too: -nullarbor:~ # ( -> echo 'file svcsock.c line 1603 +p' ;\ -> echo 'file svcsock.c line 1563 +p' ;\ -> ) > /proc/dprintk + ~# cat query-batch-file > /dynamic_debug/control At the syntactical level, a command comprises a sequence of match specifications, followed by a flags change specification. diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c index 93fc5d5..310c753 100644 --- a/lib/dynamic_debug.c +++ b/lib/dynamic_debug.c @@ -124,13 +124,13 @@ do { \ } while (0) /* - * Search the tables for _ddebug's which match the given - * `query' and apply the `flags' and `mask' to them. Tells - * the user which ddebug's were changed, or whether none - * were matched. + * Search the tables for _ddebug's which match the given `query' and + * apply the `flags' and `mask' to them. Returns number of matching + * callsites, normally the same as number of changes. If verbose, + * logs the changes. Takes ddebug_lock. */ -static void ddebug_change(const struct ddebug_query *query, - unsigned int flags, unsigned int mask) +static int ddebug_change(const struct ddebug_query *query, + unsigned int flags, unsigned int mask) { int i; struct ddebug_table *dt; @@ -192,6 +192,8 @@ static void ddebug_change(const struct ddebug_query *query, if (!nfound && verbose) pr_info("no matches for query\n"); + + return nfound; } /* @@ -449,7 +451,7 @@ static int ddebug_exec_query(char *query_string) unsigned int flags = 0, mask = 0; struct ddebug_query query; #define MAXWORDS 9 - int nwords; + int nwords, nfound; char *words[MAXWORDS]; nwords = ddebug_tokenize(query_string, words, MAXWORDS); @@ -461,8 +463,47 @@ static int ddebug_exec_query(char *query_string) return -EINVAL; /* actually go and implement the change */ - ddebug_change(&query, flags, mask); - return 0; + nfound = ddebug_change(&query, flags, mask); + vpr_info_dq((&query), (nfound) ? "applied" : "no-match"); + + return nfound; +} + +/* handle multiple queries in query string, continue on error, return + last error or number of matching callsites. Module name is either + in param (for boot arg) or perhaps in query string. +*/ +static int ddebug_exec_queries(char *query) +{ + char *split; + int i, errs = 0, exitcode = 0, rc, nfound = 0; + + for (i = 0; query; query = split) { + split = strpbrk(query, ";\n"); + if (split) + *split++ = '\0'; + + query = skip_spaces(query); + if (!query || !*query || *query == '#') + continue; + + if (verbose) + pr_info("query %d: \"%s\"\n", i, query); + + rc = ddebug_exec_query(query); + if (rc < 0) { + errs++; + exitcode = rc; + } else + nfound += rc; + i++; + } + pr_info("processed %d queries, with %d matches, %d errs\n", + i, nfound, errs); + + if (exitcode) + return exitcode; + return nfound; } #define PREFIX_SIZE 64 @@ -615,9 +656,9 @@ static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf, if (verbose) pr_info("read %d bytes from userspace\n", (int)len); - ret = ddebug_exec_query(tmpbuf); + ret = ddebug_exec_queries(tmpbuf); kfree(tmpbuf); - if (ret) + if (ret < 0) return ret; *offp += len; @@ -927,13 +968,15 @@ static int __init dynamic_debug_init(void) /* ddebug_query boot param got passed -> set it up */ if (ddebug_setup_string[0] != '\0') { - ret = ddebug_exec_query(ddebug_setup_string); - if (ret) + ret = ddebug_exec_queries(ddebug_setup_string); + if (ret < 0) pr_warn("Invalid ddebug boot param %s", ddebug_setup_string); else - pr_info("ddebug initialized with string %s", - ddebug_setup_string); + pr_info("%d changes by ddebug_query\n", ret); + + /* keep tables even on ddebug_query parse error */ + ret = 0; } out_free: -- cgit v1.1 From 2897a563a55442379e5e59ec68c229a7f27fb7c6 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Sun, 8 Jan 2012 10:12:18 -0800 Subject: drivers: hv: Get rid of some unnecessary code The current code unnecessarily limits the number of offers we handle. Get rid of this limitation. As part of this cleanup, also get rid of an unused define - MAX_MSG_TYPES. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 87 ----------------------------------------------- 1 file changed, 87 deletions(-) diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 36484db..9ffbfc5 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -37,81 +37,6 @@ struct vmbus_channel_message_table_entry { void (*message_handler)(struct vmbus_channel_message_header *msg); }; -#define MAX_MSG_TYPES 4 -#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 8 - -static const uuid_le - supported_device_classes[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = { - /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */ - /* Storage - SCSI */ - { - .b = { - 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, - 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f - } - }, - - /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */ - /* Network */ - { - .b = { - 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, - 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E - } - }, - - /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */ - /* Input */ - { - .b = { - 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, - 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A - } - }, - - /* {32412632-86cb-44a2-9b5c-50d1417354f5} */ - /* IDE */ - { - .b = { - 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, - 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5 - } - }, - /* 0E0B6031-5213-4934-818B-38D90CED39DB */ - /* Shutdown */ - { - .b = { - 0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49, - 0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB - } - }, - /* {9527E630-D0AE-497b-ADCE-E80AB0175CAF} */ - /* TimeSync */ - { - .b = { - 0x30, 0xe6, 0x27, 0x95, 0xae, 0xd0, 0x7b, 0x49, - 0xad, 0xce, 0xe8, 0x0a, 0xb0, 0x17, 0x5c, 0xaf - } - }, - /* {57164f39-9115-4e78-ab55-382f3bd5422d} */ - /* Heartbeat */ - { - .b = { - 0x39, 0x4f, 0x16, 0x57, 0x15, 0x91, 0x78, 0x4e, - 0xab, 0x55, 0x38, 0x2f, 0x3b, 0xd5, 0x42, 0x2d - } - }, - /* {A9A0F4E7-5A45-4d96-B827-8A841E8C03E6} */ - /* KVP */ - { - .b = { - 0xe7, 0xf4, 0xa0, 0xa9, 0x45, 0x5a, 0x96, 0x4d, - 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x3, 0xe6 - } - }, - -}; - /** * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message @@ -321,20 +246,8 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) struct vmbus_channel *newchannel; uuid_le *guidtype; uuid_le *guidinstance; - int i; - int fsupported = 0; offer = (struct vmbus_channel_offer_channel *)hdr; - for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) { - if (!uuid_le_cmp(offer->offer.if_type, - supported_device_classes[i])) { - fsupported = 1; - break; - } - } - - if (!fsupported) - return; guidtype = &offer->offer.if_type; guidinstance = &offer->offer.if_instance; -- cgit v1.1 From c56d8a7362665d165ba992b6b7a8d6c13a26eafc Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Tue, 17 Jan 2012 12:17:22 +0000 Subject: sysfs: change permissions for /sys from 0755 to 0555 There is a misleading difference between /proc and /sys permissions, /proc is 0555 and /sys is 0755. But as it is impossible to create or unlink something in /sys it would be nice to have same permissions. Signed-off-by: Vitaly Kuznetsov Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index e34f0d9..140f26a 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -36,7 +36,7 @@ struct sysfs_dirent sysfs_root = { .s_name = "", .s_count = ATOMIC_INIT(1), .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), - .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, + .s_mode = S_IFDIR | S_IRUGO | S_IXUGO, .s_ino = 1, }; -- cgit v1.1 From 2b31594a9523449b168946725689d039c80204de Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Sat, 14 Jan 2012 11:06:03 +0900 Subject: driver-core: Fix possible null reference in subsys_interface_unregister Check if the sif is not NULL before de-referencing it Signed-off-by: Jonghwan Choi Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 99dc592..4ddb38b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -1193,13 +1193,15 @@ EXPORT_SYMBOL_GPL(subsys_interface_register); void subsys_interface_unregister(struct subsys_interface *sif) { - struct bus_type *subsys = sif->subsys; + struct bus_type *subsys; struct subsys_dev_iter iter; struct device *dev; - if (!sif) + if (!sif || !sif->subsys) return; + subsys = sif->subsys; + mutex_lock(&subsys->p->mutex); list_del_init(&sif->node); if (sif->remove_dev) { -- cgit v1.1 From fde25a9b63b9a3dc91365c394a426ebe64cfc2da Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 13:34:24 -0500 Subject: Driver core: driver_find() drops reference before returning As part of the removal of get_driver()/put_driver(), this patch (as1510) changes driver_find(); it now drops the reference it acquires before returning. The patch also adjusts all the callers of driver_find() to remove the now unnecessary calls to put_driver(). In addition, the patch adds a warning to driver_find(): Callers must make sure the driver they are searching for does not get unloaded while they are using it. This has always been the case; driver_find() has never prevented a driver from being unregistered or unloaded. Hence the patch will not introduce any new bugs. The existing callers all seem to be okay in this respect, however I don't understand the video drivers well enough to be certain about them. Signed-off-by: Alan Stern CC: Dmitry Torokhov CC: Kyungmin Park CC: Andy Walls CC: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- drivers/base/driver.c | 7 +++++-- drivers/input/gameport/gameport.c | 1 - drivers/input/serio/serio.c | 1 - drivers/media/video/cx18/cx18-alsa-main.c | 1 - drivers/media/video/ivtv/ivtvfb.c | 2 -- drivers/media/video/s5p-fimc/fimc-mdevice.c | 5 +---- drivers/media/video/s5p-tv/mixer_video.c | 1 - drivers/s390/net/smsgiucv_app.c | 9 ++++----- 8 files changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index b631f7c..e979cad 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -234,7 +234,6 @@ int driver_register(struct device_driver *drv) other = driver_find(drv->name, drv->bus); if (other) { - put_driver(other); printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n", drv->name); return -EBUSY; @@ -275,7 +274,9 @@ EXPORT_SYMBOL_GPL(driver_unregister); * Call kset_find_obj() to iterate over list of drivers on * a bus to find driver by name. Return driver if found. * - * Note that kset_find_obj increments driver's reference count. + * This routine provides no locking to prevent the driver it returns + * from being unregistered or unloaded while the caller is using it. + * The caller is responsible for preventing this. */ struct device_driver *driver_find(const char *name, struct bus_type *bus) { @@ -283,6 +284,8 @@ struct device_driver *driver_find(const char *name, struct bus_type *bus) struct driver_private *priv; if (k) { + /* Drop reference added by kset_find_obj() */ + kobject_put(k); priv = to_driver(k); return priv->driver; } diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index c351aa4..da739d9 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -449,7 +449,6 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); error = gameport_bind_driver(gameport, to_gameport_driver(drv)); - put_driver(drv); } else { error = -EINVAL; } diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index ba70058..d0f7533 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -441,7 +441,6 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { serio_disconnect_port(serio); error = serio_bind_driver(serio, to_serio_driver(drv)); - put_driver(drv); serio_remove_duplicate_events(serio, SERIO_RESCAN_PORT); } else { error = -EINVAL; diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index a1e6c2a..e118361 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -285,7 +285,6 @@ static void __exit cx18_alsa_exit(void) drv = driver_find("cx18", &pci_bus_type); ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); - put_driver(drv); cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index d0fbfcf..e5e7fa9 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1293,7 +1293,6 @@ static int __init ivtvfb_init(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); - put_driver(drv); if (!registered) { printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; @@ -1310,7 +1309,6 @@ static void ivtvfb_cleanup(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); - put_driver(drv); } module_init(ivtvfb_init); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 8ea4ee1..63eccb5 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -344,16 +344,13 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd) return -ENODEV; ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); - put_driver(driver); if (ret) return ret; driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) { + if (driver) ret = driver_for_each_device(driver, NULL, fmd, csis_register_callback); - put_driver(driver); - } return ret; } diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 7884bae..f7ca5cc 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -58,7 +58,6 @@ static struct v4l2_subdev *find_and_register_subdev( } done: - put_driver(drv); return sd; } diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c index 4d2ea40..32515a2 100644 --- a/drivers/s390/net/smsgiucv_app.c +++ b/drivers/s390/net/smsgiucv_app.c @@ -168,7 +168,7 @@ static int __init smsgiucv_app_init(void) rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT); if (rc) { kfree(smsg_app_dev); - goto fail_put_driver; + goto fail; } smsg_app_dev->bus = &iucv_bus; smsg_app_dev->parent = iucv_root; @@ -177,7 +177,7 @@ static int __init smsgiucv_app_init(void) rc = device_register(smsg_app_dev); if (rc) { put_device(smsg_app_dev); - goto fail_put_driver; + goto fail; } /* convert sender to uppercase characters */ @@ -191,12 +191,11 @@ static int __init smsgiucv_app_init(void) rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback); if (rc) { device_unregister(smsg_app_dev); - goto fail_put_driver; + goto fail; } rc = 0; -fail_put_driver: - put_driver(smsgiucv_drv); +fail: return rc; } module_init(smsgiucv_app_init); -- cgit v1.1 From cef9bc56e1e944afd11f96de569657117a138c6d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 13:34:41 -0500 Subject: Dynamic ID addition doesn't need get_driver() As part of the removal of get_driver()/put_driver(), this patch (as1511) changes all the places that add dynamic IDs for drivers. Since these additions are done by writing to the drivers' sysfs attribute files, and the attributes are removed when the drivers are unregistered, there is no reason to take an extra reference to the drivers. The one exception is the pci-stub driver, which calls pci_add_dynid() as part of its registration. But again, there's no reason to take an extra reference here, because the driver can't be unloaded while it is being registered. Signed-off-by: Alan Stern CC: Dmitry Torokhov CC: Jiri Kosina CC: Jesse Barnes CC: Dominik Brodowski Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-core.c | 6 +----- drivers/pci/pci-driver.c | 2 -- drivers/pcmcia/ds.c | 5 +---- drivers/usb/core/driver.c | 5 +---- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index af08ce7..bce53fa 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1619,11 +1619,7 @@ static ssize_t store_new_id(struct device_driver *drv, const char *buf, list_add_tail(&dynid->list, &hdrv->dyn_list); spin_unlock(&hdrv->dyn_lock); - ret = 0; - if (get_driver(&hdrv->driver)) { - ret = driver_attach(&hdrv->driver); - put_driver(&hdrv->driver); - } + ret = driver_attach(&hdrv->driver); return ret ? : count; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3623d65..ff54047 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -72,9 +72,7 @@ int pci_add_dynid(struct pci_driver *drv, list_add_tail(&dynid->node, &drv->dynids.list); spin_unlock(&drv->dynids.lock); - get_driver(&drv->driver); retval = driver_attach(&drv->driver); - put_driver(&drv->driver); return retval; } diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 749c2a1..059699f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -127,10 +127,7 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count) list_add_tail(&dynid->node, &pdrv->dynids.list); mutex_unlock(&pdrv->dynids.lock); - if (get_driver(&pdrv->drv)) { - retval = driver_attach(&pdrv->drv); - put_driver(&pdrv->drv); - } + retval = driver_attach(&pdrv->drv); if (retval) return retval; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d40ff95..54c493b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -71,10 +71,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, list_add_tail(&dynid->node, &dynids->list); spin_unlock(&dynids->lock); - if (get_driver(driver)) { - retval = driver_attach(driver); - put_driver(driver); - } + retval = driver_attach(driver); if (retval) return retval; -- cgit v1.1 From 9f30ea950edfaefa51221dd26a065b3442599778 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 24 Jan 2012 13:35:02 -0500 Subject: cio: remove {get,put}_driver Remove useless {get,put}_driver - the caller of the functions has to ensure valid driver pointers. Signed-off-by: Sebastian Ott Signed-off-by: Alan Stern CC: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- drivers/s390/cio/ccwgroup.c | 2 -- drivers/s390/cio/device.c | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 4f1989d..5f1dc6f 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -580,7 +580,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) struct device *dev; /* We don't want ccwgroup devices to live longer than their driver. */ - get_driver(&cdriver->driver); while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, __ccwgroup_match_all))) { struct ccwgroup_device *gdev = to_ccwgroupdev(dev); @@ -592,7 +591,6 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) mutex_unlock(&gdev->reg_mutex); put_device(dev); } - put_driver(&cdriver->driver); driver_unregister(&cdriver->driver); } EXPORT_SYMBOL(ccwgroup_driver_unregister); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 4726985..02d0152 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1676,15 +1676,9 @@ struct ccw_device *get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id) { struct device *dev; - struct device_driver *drv; - drv = get_driver(&cdrv->driver); - if (!drv) - return NULL; - - dev = driver_find_device(drv, NULL, (void *)bus_id, + dev = driver_find_device(&cdrv->driver, NULL, (void *)bus_id, __ccwdev_check_busid); - put_driver(drv); return dev ? to_ccwdev(dev) : NULL; } -- cgit v1.1 From f3ff9247088a0af0c192a28908dab76ff3d8871f Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 13:35:24 -0500 Subject: Remove useless get_driver()/put_driver() calls As part of the removal of get_driver()/put_driver(), this patch (as1512) gets rid of various useless and unnecessary calls in several drivers. In some cases it may be desirable to pin the driver by calling try_module_get(), but that can be done later. Signed-off-by: Alan Stern CC: "David S. Miller" CC: Konrad Rzeszutek Wilk CC: Michael Buesch CC: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/phy_device.c | 6 +----- drivers/pci/xen-pcifront.c | 3 +-- drivers/ssb/main.c | 20 ++------------------ lib/dma-debug.c | 3 +-- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index f320f46..e8c42d6 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -915,9 +915,7 @@ static int phy_probe(struct device *dev) phydev = to_phy_device(dev); - /* Make sure the driver is held. - * XXX -- Is this correct? */ - drv = get_driver(phydev->dev.driver); + drv = phydev->dev.driver; phydrv = to_phy_driver(drv); phydev->drv = phydrv; @@ -957,8 +955,6 @@ static int phy_remove(struct device *dev) if (phydev->drv->remove) phydev->drv->remove(phydev); - - put_driver(dev->driver); phydev->drv = NULL; return 0; diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 7cf3d2f..6f81998 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -593,7 +593,7 @@ static pci_ers_result_t pcifront_common_process(int cmd, } pdrv = pcidev->driver; - if (get_driver(&pdrv->driver)) { + if (pdrv->driver) { if (pdrv->err_handler && pdrv->err_handler->error_detected) { dev_dbg(&pcidev->dev, "trying to call AER service\n"); @@ -623,7 +623,6 @@ static pci_ers_result_t pcifront_common_process(int cmd, } } } - put_driver(&pdrv->driver); } if (!flag) result = PCI_ERS_RESULT_NONE; diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index bb6317f..ff109ae 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -140,19 +140,6 @@ static void ssb_device_put(struct ssb_device *dev) put_device(dev->dev); } -static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv) -{ - if (drv) - get_driver(&drv->drv); - return drv; -} - -static inline void ssb_driver_put(struct ssb_driver *drv) -{ - if (drv) - put_driver(&drv->drv); -} - static int ssb_device_resume(struct device *dev) { struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); @@ -250,11 +237,9 @@ int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) ssb_device_put(sdev); continue; } - sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver)); - if (!sdrv || SSB_WARN_ON(!sdrv->remove)) { - ssb_device_put(sdev); + sdrv = drv_to_ssb_drv(sdev->dev->driver); + if (SSB_WARN_ON(!sdrv->remove)) continue; - } sdrv->remove(sdev); ctx->device_frozen[i] = 1; } @@ -293,7 +278,6 @@ int ssb_devices_thaw(struct ssb_freeze_context *ctx) dev_name(sdev->dev)); result = err; } - ssb_driver_put(sdrv); ssb_device_put(sdev); } diff --git a/lib/dma-debug.c b/lib/dma-debug.c index fea790a..13ef233 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c @@ -170,7 +170,7 @@ static bool driver_filter(struct device *dev) return false; /* driver filter on but not yet initialized */ - drv = get_driver(dev->driver); + drv = dev->driver; if (!drv) return false; @@ -185,7 +185,6 @@ static bool driver_filter(struct device *dev) } read_unlock_irqrestore(&driver_name_lock, flags); - put_driver(drv); return ret; } -- cgit v1.1 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(-) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index e979cad..60e4f77 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 a782d7f..d28bd82 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.1 From d6e486868cde585842d55ba3b6ec57af090fc343 Mon Sep 17 00:00:00 2001 From: Ludwig Nussel Date: Wed, 25 Jan 2012 11:52:28 +0100 Subject: debugfs: add mode, uid and gid options Cautious admins may want to restrict access to debugfs. Currently a manual chown/chmod e.g. in an init script is needed to achieve that. Distributions that want to make the mount options configurable need to add extra config files. By allowing to set the root inode's uid, gid and mode via mount options no such hacks are needed anymore. Instead configuration becomes straight forward via fstab. Signed-off-by: Ludwig Nussel Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 5 +- fs/debugfs/inode.c | 149 +++++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 6872c91..4e25758 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -14,7 +14,10 @@ Debugfs is typically mounted with a command like: mount -t debugfs none /sys/kernel/debug -(Or an equivalent /etc/fstab line). +(Or an equivalent /etc/fstab line). +The debugfs root directory is accessible by anyone by default. To +restrict access to the tree the "uid", "gid" and "mode" mount +options can be used. Note that the debugfs API is exported GPL-only to modules. diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 956d5dd..b80bc84 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -23,9 +23,13 @@ #include #include #include +#include +#include #include #include +#define DEBUGFS_DEFAULT_MODE 0755 + static struct vfsmount *debugfs_mount; static int debugfs_mount_count; static bool debugfs_registered; @@ -125,11 +129,154 @@ static inline int debugfs_positive(struct dentry *dentry) return dentry->d_inode && !d_unhashed(dentry); } +struct debugfs_mount_opts { + uid_t uid; + gid_t gid; + umode_t mode; +}; + +enum { + Opt_uid, + Opt_gid, + Opt_mode, + Opt_err +}; + +static const match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_mode, "mode=%o"}, + {Opt_err, NULL} +}; + +struct debugfs_fs_info { + struct debugfs_mount_opts mount_opts; +}; + +static int debugfs_parse_options(char *data, struct debugfs_mount_opts *opts) +{ + substring_t args[MAX_OPT_ARGS]; + int option; + int token; + char *p; + + opts->mode = DEBUGFS_DEFAULT_MODE; + + while ((p = strsep(&data, ",")) != NULL) { + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return -EINVAL; + opts->uid = option; + break; + case Opt_gid: + if (match_octal(&args[0], &option)) + return -EINVAL; + opts->gid = option; + break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return -EINVAL; + opts->mode = option & S_IALLUGO; + break; + /* + * We might like to report bad mount options here; + * but traditionally debugfs has ignored all mount options + */ + } + } + + return 0; +} + +static int debugfs_apply_options(struct super_block *sb) +{ + struct debugfs_fs_info *fsi = sb->s_fs_info; + struct inode *inode = sb->s_root->d_inode; + struct debugfs_mount_opts *opts = &fsi->mount_opts; + + inode->i_mode &= ~S_IALLUGO; + inode->i_mode |= opts->mode; + + inode->i_uid = opts->uid; + inode->i_gid = opts->gid; + + return 0; +} + +static int debugfs_remount(struct super_block *sb, int *flags, char *data) +{ + int err; + struct debugfs_fs_info *fsi = sb->s_fs_info; + + err = debugfs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + debugfs_apply_options(sb); + +fail: + return err; +} + +static int debugfs_show_options(struct seq_file *m, struct dentry *root) +{ + struct debugfs_fs_info *fsi = root->d_sb->s_fs_info; + struct debugfs_mount_opts *opts = &fsi->mount_opts; + + if (opts->uid != 0) + seq_printf(m, ",uid=%u", opts->uid); + if (opts->gid != 0) + seq_printf(m, ",gid=%u", opts->gid); + if (opts->mode != DEBUGFS_DEFAULT_MODE) + seq_printf(m, ",mode=%o", opts->mode); + + return 0; +} + +static const struct super_operations debugfs_super_operations = { + .statfs = simple_statfs, + .remount_fs = debugfs_remount, + .show_options = debugfs_show_options, +}; + static int debug_fill_super(struct super_block *sb, void *data, int silent) { static struct tree_descr debug_files[] = {{""}}; + struct debugfs_fs_info *fsi; + int err; + + save_mount_options(sb, data); + + fsi = kzalloc(sizeof(struct debugfs_fs_info), GFP_KERNEL); + sb->s_fs_info = fsi; + if (!fsi) { + err = -ENOMEM; + goto fail; + } + + err = debugfs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + err = simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); + if (err) + goto fail; + + sb->s_op = &debugfs_super_operations; + + debugfs_apply_options(sb); + + return 0; - return simple_fill_super(sb, DEBUGFS_MAGIC, debug_files); +fail: + kfree(fsi); + sb->s_fs_info = NULL; + return err; } static struct dentry *debug_mount(struct file_system_type *fs_type, -- cgit v1.1 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 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 0000000..ff501e5 --- /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 25f24dc..6ab6aa2 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 0000000..7acc961 --- /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 a524353..7c89880 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 b29e7f6..cff2cc0 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 c0e14b3..026ba38 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.1 From 3bd391f056df61e928de1680ff4a3e7e07e5b399 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:06 +0100 Subject: crypto: Add support for x86 cpuid auto loading for x86 crypto drivers Add support for auto-loading of crypto drivers based on cpuid features. This enables auto-loading of the VIA and Intel specific drivers for AES, hashing and CRCs. Requires the earlier infrastructure patch to add x86 modinfo. I kept it all in a single patch for now. I dropped the printks when the driver cpuid doesn't match (imho drivers never should print anything in such a case) One drawback is that udev doesn't know if the drivers are used or not, so they will be unconditionally loaded at boot up. That's better than not loading them at all, like it often happens. Cc: Dave Jones Cc: Kay Sievers Cc: Jen Axboe Cc: Herbert Xu Cc: Huang Ying Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/aesni-intel_glue.c | 12 +++++++++--- arch/x86/crypto/crc32c-intel.c | 11 ++++++++--- arch/x86/crypto/ghash-clmulni-intel_glue.c | 12 ++++++++---- drivers/crypto/padlock-aes.c | 9 ++++++++- drivers/crypto/padlock-sha.c | 16 ++++++++-------- 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index 545d0ce..b3350bd 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1253,14 +1254,19 @@ static struct crypto_alg __rfc4106_alg = { }; #endif + +static const struct x86_cpu_id aesni_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_AES), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, aesni_cpu_id); + static int __init aesni_init(void) { int err; - if (!cpu_has_aes) { - printk(KERN_INFO "Intel AES-NI instructions are not detected.\n"); + if (!x86_match_cpu(aesni_cpu_id)) return -ENODEV; - } if ((err = crypto_fpu_init())) goto fpu_err; diff --git a/arch/x86/crypto/crc32c-intel.c b/arch/x86/crypto/crc32c-intel.c index b9d0026..493f959 100644 --- a/arch/x86/crypto/crc32c-intel.c +++ b/arch/x86/crypto/crc32c-intel.c @@ -31,6 +31,7 @@ #include #include +#include #define CHKSUM_BLOCK_SIZE 1 #define CHKSUM_DIGEST_SIZE 4 @@ -173,13 +174,17 @@ static struct shash_alg alg = { } }; +static const struct x86_cpu_id crc32c_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_XMM4_2), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, crc32c_cpu_id); static int __init crc32c_intel_mod_init(void) { - if (cpu_has_xmm4_2) - return crypto_register_shash(&alg); - else + if (!x86_match_cpu(crc32c_cpu_id)) return -ENODEV; + return crypto_register_shash(&alg); } static void __exit crc32c_intel_mod_fini(void) diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c index 976aa64..b4bf0a6 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_glue.c +++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c @@ -20,6 +20,7 @@ #include #include #include +#include #define GHASH_BLOCK_SIZE 16 #define GHASH_DIGEST_SIZE 16 @@ -294,15 +295,18 @@ static struct ahash_alg ghash_async_alg = { }, }; +static const struct x86_cpu_id pcmul_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_PCLMULQDQ), /* Pickle-Mickle-Duck */ + {} +}; +MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id); + static int __init ghash_pclmulqdqni_mod_init(void) { int err; - if (!cpu_has_pclmulqdq) { - printk(KERN_INFO "Intel PCLMULQDQ-NI instructions are not" - " detected.\n"); + if (!x86_match_cpu(pcmul_cpu_id)) return -ENODEV; - } err = crypto_register_shash(&ghash_alg); if (err) diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c index 29b9469..37b2e94 100644 --- a/drivers/crypto/padlock-aes.c +++ b/drivers/crypto/padlock-aes.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -503,12 +504,18 @@ static struct crypto_alg cbc_aes_alg = { } }; +static struct x86_cpu_id padlock_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_XCRYPT), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, padlock_cpu_id); + static int __init padlock_init(void) { int ret; struct cpuinfo_x86 *c = &cpu_data(0); - if (!cpu_has_xcrypt) + if (!x86_match_cpu(padlock_cpu_id)) return -ENODEV; if (!cpu_has_xcrypt_enabled) { diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index 06bdb4b..9266c0e 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -22,6 +22,7 @@ #include #include #include +#include #include struct padlock_sha_desc { @@ -526,6 +527,12 @@ static struct shash_alg sha256_alg_nano = { } }; +static struct x86_cpu_id padlock_sha_ids[] = { + X86_FEATURE_MATCH(X86_FEATURE_PHE), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, padlock_sha_ids); + static int __init padlock_init(void) { int rc = -ENODEV; @@ -533,15 +540,8 @@ static int __init padlock_init(void) struct shash_alg *sha1; struct shash_alg *sha256; - if (!cpu_has_phe) { - printk(KERN_NOTICE PFX "VIA PadLock Hash Engine not detected.\n"); - return -ENODEV; - } - - if (!cpu_has_phe_enabled) { - printk(KERN_NOTICE PFX "VIA PadLock detected, but not enabled. Hmm, strange...\n"); + if (!x86_match_cpu(padlock_sha_ids) || !cpu_has_phe_enabled) return -ENODEV; - } /* Register the newly added algorithm module if on * * VIA Nano processor, or else just do as before */ -- cgit v1.1 From b66b8b9a4a79087dde1b358a016e5c8739ccf186 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:07 +0100 Subject: intel-idle: convert to x86_cpu_id auto probing With this it should be automatically loaded on suitable systems by udev. The old switch () is replaced with a table based approach, this also cleans up the code. Cc: Len Brown Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/idle/intel_idle.c | 116 ++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 20bce51..ef6a409 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,17 @@ static unsigned int mwait_substates; /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ +struct idle_cpu { + struct cpuidle_state *state_table; + + /* + * Hardware C-state auto-demotion may not always be optimal. + * Indicate which enable bits to clear here. + */ + unsigned long auto_demotion_disable_flags; +}; + +static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); @@ -88,12 +100,6 @@ static int intel_idle(struct cpuidle_device *dev, static struct cpuidle_state *cpuidle_state_table; /* - * Hardware C-state auto-demotion may not always be optimal. - * Indicate which enable bits to clear here. - */ -static unsigned long long auto_demotion_disable_flags; - -/* * Set this flag for states where the HW flushes the TLB for us * and so we don't need cross-calls to keep it consistent. * If this flag is set, SW flushes the TLB, so even if the @@ -319,27 +325,72 @@ static void auto_demotion_disable(void *dummy) unsigned long long msr_bits; rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); - msr_bits &= ~auto_demotion_disable_flags; + msr_bits &= ~(icpu->auto_demotion_disable_flags); wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); } +static const struct idle_cpu idle_cpu_nehalem = { + .state_table = nehalem_cstates, +}; + +static const struct idle_cpu idle_cpu_westmere = { + .state_table = nehalem_cstates, + .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, +}; + +static const struct idle_cpu idle_cpu_atom = { + .state_table = atom_cstates, +}; + +static const struct idle_cpu idle_cpu_lincroft = { + .state_table = atom_cstates, + .auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE, +}; + +static const struct idle_cpu idle_cpu_snb = { + .state_table = snb_cstates, +}; + +#define ICPU(model, cpu) \ + { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } + +static const struct x86_cpu_id intel_idle_ids[] = { + ICPU(0x1a, idle_cpu_nehalem), + ICPU(0x1e, idle_cpu_nehalem), + ICPU(0x1f, idle_cpu_nehalem), + ICPU(0x25, idle_cpu_westmere), + ICPU(0x2c, idle_cpu_westmere), + ICPU(0x2f, idle_cpu_westmere), + ICPU(0x1c, idle_cpu_atom), + ICPU(0x26, idle_cpu_lincroft), + ICPU(0x2f, idle_cpu_westmere), + ICPU(0x2a, idle_cpu_snb), + ICPU(0x2d, idle_cpu_snb), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids); + /* * intel_idle_probe() */ static int intel_idle_probe(void) { unsigned int eax, ebx, ecx; + const struct x86_cpu_id *id; if (max_cstate == 0) { pr_debug(PREFIX "disabled\n"); return -EPERM; } - if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) - return -ENODEV; - - if (!boot_cpu_has(X86_FEATURE_MWAIT)) + id = x86_match_cpu(intel_idle_ids); + if (!id) { + if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && + boot_cpu_data.x86 == 6) + pr_debug(PREFIX "does not run on family %d model %d\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); return -ENODEV; + } if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) return -ENODEV; @@ -353,43 +404,8 @@ static int intel_idle_probe(void) pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates); - - if (boot_cpu_data.x86 != 6) /* family 6 */ - return -ENODEV; - - switch (boot_cpu_data.x86_model) { - - case 0x1A: /* Core i7, Xeon 5500 series */ - case 0x1E: /* Core i7 and i5 Processor - Lynnfield Jasper Forest */ - case 0x1F: /* Core i7 and i5 Processor - Nehalem */ - case 0x2E: /* Nehalem-EX Xeon */ - case 0x2F: /* Westmere-EX Xeon */ - case 0x25: /* Westmere */ - case 0x2C: /* Westmere */ - cpuidle_state_table = nehalem_cstates; - auto_demotion_disable_flags = - (NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE); - break; - - case 0x1C: /* 28 - Atom Processor */ - cpuidle_state_table = atom_cstates; - break; - - case 0x26: /* 38 - Lincroft Atom Processor */ - cpuidle_state_table = atom_cstates; - auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE; - break; - - case 0x2A: /* SNB */ - case 0x2D: /* SNB Xeon */ - cpuidle_state_table = snb_cstates; - break; - - default: - pr_debug(PREFIX "does not run on family %d model %d\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - return -ENODEV; - } + icpu = (const struct idle_cpu *)id->driver_data; + cpuidle_state_table = icpu->state_table; if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; @@ -470,7 +486,7 @@ static int intel_idle_cpuidle_driver_init(void) drv->state_count += 1; } - if (auto_demotion_disable_flags) + if (icpu->auto_demotion_disable_flags) on_each_cpu(auto_demotion_disable, NULL, 1); return 0; @@ -522,7 +538,7 @@ int intel_idle_cpu_init(int cpu) return -EIO; } - if (auto_demotion_disable_flags) + if (icpu->auto_demotion_disable_flags) smp_call_function_single(cpu, auto_demotion_disable, NULL, 1); return 0; -- cgit v1.1 From 9061e0e16700ef228837e96987ff51794c956197 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:08 +0100 Subject: ACPI: Load acpi-cpufreq from processor driver automatically The only left over hole in automatic cpufreq driver loading was the loading of ACPI cpufreq. This driver should be loaded when ACPI supports a _PDC method and the CPU vendor wants to use acpi cpufreq. Simply add a request module call to the acpi processor core driver when this is true. This seems like the simplest solution for this. Cc: Len Brown Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/processor_driver.c | 1 + drivers/acpi/processor_perflib.c | 22 ++++++++++++++++++++++ include/acpi/processor.h | 1 + 3 files changed, 24 insertions(+) diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0034ede..e6920d0 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -497,6 +497,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_load_module(pr); #endif acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 85b3237..0af48a8 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -240,6 +240,28 @@ void acpi_processor_ppc_exit(void) acpi_processor_ppc_status &= ~PPC_REGISTERED; } +/* + * Do a quick check if the systems looks like it should use ACPI + * cpufreq. We look at a _PCT method being available, but don't + * do a whole lot of sanity checks. + */ +void acpi_processor_load_module(struct acpi_processor *pr) +{ + static int requested; + acpi_status status = 0; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (!arch_has_acpi_pdc() || requested) + return; + status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer); + if (!ACPI_FAILURE(status)) { + printk(KERN_INFO PREFIX "Requesting acpi_cpufreq\n"); + request_module_nowait("acpi_cpufreq"); + requested = 1; + } + kfree(buffer.pointer); +} + static int acpi_processor_get_performance_control(struct acpi_processor *pr) { int result = 0; diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 610f6fb..da57fdc 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -224,6 +224,7 @@ struct acpi_processor_errata { } piix4; }; +extern void acpi_processor_load_module(struct acpi_processor *pr); extern int acpi_processor_preregister_performance(struct acpi_processor_performance __percpu *performance); -- cgit v1.1 From 267fc9788d0cdb77edafb506063f06961e1418f5 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:09 +0100 Subject: HWMON: Convert via-cputemp to x86 cpuid autoprobing Use the new x86 cpuid autoprobe interface. Cc: Jean Delvare Cc: Guenter Roeck Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/via-cputemp.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 8eac67d..8689664e 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -37,6 +37,7 @@ #include #include #include +#include #define DRVNAME "via_cputemp" @@ -308,15 +309,20 @@ static struct notifier_block via_cputemp_cpu_notifier __refdata = { .notifier_call = via_cputemp_cpu_callback, }; +static const struct x86_cpu_id cputemp_ids[] = { + { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ + { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ + { X86_VENDOR_CENTAUR, 6, 0xf, }, /* Nano */ + {} +}; +MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); + static int __init via_cputemp_init(void) { int i, err; - if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) { - printk(KERN_DEBUG DRVNAME ": Not a VIA CPU\n"); - err = -ENODEV; - goto exit; - } + if (!x86_match_cpu(cputemp_ids)) + return -ENODEV; err = platform_driver_register(&via_cputemp_driver); if (err) -- cgit v1.1 From 9b38096fde5f9b93c3657911c3be7892cc155cbd Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:10 +0100 Subject: HWMON: Convert coretemp to x86 cpuid autoprobing Use the new x86 cpuid autoprobe interface for the Intel coretemp driver. Cc: Fenghua Yu Cc: Jean Delvare Cc: Guenter Roeck Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/coretemp.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index a6c6ec3..249ac46 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -39,6 +39,7 @@ #include #include #include +#include #define DRVNAME "coretemp" @@ -759,13 +760,23 @@ static struct notifier_block coretemp_cpu_notifier __refdata = { .notifier_call = coretemp_cpu_callback, }; +static const struct x86_cpu_id coretemp_ids[] = { + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTS }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); + static int __init coretemp_init(void) { int i, err = -ENODEV; - /* quick check if we run Intel */ - if (cpu_data(0).x86_vendor != X86_VENDOR_INTEL) - goto exit; + /* + * CPUID.06H.EAX[0] indicates whether the CPU has thermal + * sensors. We check this bit only, all the early CPUs + * without thermal sensors will be filtered out. + */ + if (!x86_match_cpu(coretemp_ids)) + return -ENODEV; err = platform_driver_register(&coretemp_driver); if (err) -- cgit v1.1 From 2f1e097e24defe64a86535b53768f5c8ab0368d1 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 26 Jan 2012 00:09:11 +0100 Subject: X86: Introduce HW-Pstate scattered cpuid feature It is rather similar to CPB (boot capability) feature and exists since fam10h (can be looked up in AMD's BKDG). The feature is needed for powernow-k8 to cleanup init functions and to provide proper autoloading matching with the new x86cpu modalias feature. Cc: Kay Sievers Cc: Dave Jones Cc: Borislav Petkov Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/cpufeature.h | 1 + arch/x86/kernel/cpu/scattered.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 17c5d4b..67b0910 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -176,6 +176,7 @@ #define X86_FEATURE_PLN (7*32+ 5) /* Intel Power Limit Notification */ #define X86_FEATURE_PTS (7*32+ 6) /* Intel Package Thermal Status */ #define X86_FEATURE_DTS (7*32+ 7) /* Digital Thermal Sensor */ +#define X86_FEATURE_HW_PSTATE (7*32+ 8) /* AMD HW-PState */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW (8*32+ 0) /* Intel TPR Shadow */ diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c index c7f64e6..addf9e8 100644 --- a/arch/x86/kernel/cpu/scattered.c +++ b/arch/x86/kernel/cpu/scattered.c @@ -40,6 +40,7 @@ void __cpuinit init_scattered_cpuid_features(struct cpuinfo_x86 *c) { X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 }, { X86_FEATURE_XSAVEOPT, CR_EAX, 0, 0x0000000d, 1 }, { X86_FEATURE_CPB, CR_EDX, 9, 0x80000007, 0 }, + { X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 }, { X86_FEATURE_NPT, CR_EDX, 0, 0x8000000a, 0 }, { X86_FEATURE_LBRV, CR_EDX, 1, 0x8000000a, 0 }, { X86_FEATURE_SVML, CR_EDX, 2, 0x8000000a, 0 }, -- cgit v1.1 From fa8031aefec0cf7ea6c2387c93610d99d9659aa2 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:12 +0100 Subject: cpufreq: Add support for x86 cpuinfo auto loading v4 This marks all the x86 cpuinfo tables to the CPU specific device drivers, to allow auto loading by udev. This should simplify the distribution startup scripts for this greatly. I didn't add MODULE_DEVICE_IDs to the centrino and p4-clockmod drivers, because those probably shouldn't be auto loaded and the acpi driver be used instead (not fully sure on that, would appreciate feedback) The old nforce drivers autoload based on the PCI ID. ACPI cpufreq is autoloaded in another patch. v3: Autoload gx based on PCI IDs only. Remove cpu check (Dave Jones) v4: Use newly introduce HW_PSTATE feature for powernow-k8 loading Cc: Dave Jones Cc: Kay Sievers Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq-nforce2.c | 8 ++++++++ drivers/cpufreq/e_powersaver.c | 20 +++++++++++--------- drivers/cpufreq/elanfreq.c | 14 +++++++------- drivers/cpufreq/gx-suspmod.c | 9 ++------- drivers/cpufreq/longhaul.c | 8 +++++++- drivers/cpufreq/longrun.c | 13 ++++++++----- drivers/cpufreq/p4-clockmod.c | 17 +++++++++++------ drivers/cpufreq/powernow-k6.c | 12 ++++++++---- drivers/cpufreq/powernow-k7.c | 14 ++++++++------ drivers/cpufreq/powernow-k8.c | 19 +++++++++++++------ drivers/cpufreq/sc520_freq.c | 14 ++++++++------ drivers/cpufreq/speedstep-centrino.c | 24 ++++++++++++++++++++---- drivers/cpufreq/speedstep-ich.c | 15 +++++++++++++++ drivers/cpufreq/speedstep-lib.c | 1 + drivers/cpufreq/speedstep-smi.c | 15 +++++++++++++++ 15 files changed, 142 insertions(+), 61 deletions(-) diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index 7bac808..13d311e 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -385,6 +385,14 @@ static struct cpufreq_driver nforce2_driver = { .owner = THIS_MODULE, }; +#ifdef MODULE +static DEFINE_PCI_DEVICE_TABLE(nforce2_ids) = { + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2 }, + {} +}; +MODULE_DEVICE_TABLE(pci, nforce2_ids); +#endif + /** * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic * diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 4bd6815..3fffbe6 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -437,18 +438,19 @@ static struct cpufreq_driver eps_driver = { .attr = eps_attr, }; + +/* This driver will work only on Centaur C7 processors with + * Enhanced SpeedStep/PowerSaver registers */ +static const struct x86_cpu_id eps_cpu_id[] = { + { X86_VENDOR_CENTAUR, 6, X86_MODEL_ANY, X86_FEATURE_EST }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id); + static int __init eps_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); - - /* This driver will work only on Centaur C7 processors with - * Enhanced SpeedStep/PowerSaver registers */ - if (c->x86_vendor != X86_VENDOR_CENTAUR - || c->x86 != 6 || c->x86_model < 10) - return -ENODEV; - if (!cpu_has(c, X86_FEATURE_EST)) + if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10) return -ENODEV; - if (cpufreq_register_driver(&eps_driver)) return -EINVAL; return 0; diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index c587db4..960671f 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -277,17 +278,16 @@ static struct cpufreq_driver elanfreq_driver = { .attr = elanfreq_attr, }; +static const struct x86_cpu_id elan_id[] = { + { X86_VENDOR_AMD, 4, 10, }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, elan_id); static int __init elanfreq_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); - - /* Test if we have the right hardware */ - if ((c->x86_vendor != X86_VENDOR_AMD) || - (c->x86 != 4) || (c->x86_model != 10)) { - printk(KERN_INFO "elanfreq: error: no Elan processor found!\n"); + if (!x86_match_cpu(elan_id)) return -ENODEV; - } return cpufreq_register_driver(&elanfreq_driver); } diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index ffe1f2c..5a06c0b 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -82,6 +82,7 @@ #include #include +#include #include /* PCI config registers, all at F0 */ @@ -171,6 +172,7 @@ static struct pci_device_id gx_chipset_tbl[] __initdata = { { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, { 0, }, }; +MODULE_DEVICE_TABLE(gx_chipset_tbl); static void gx_write_byte(int reg, int value) { @@ -185,13 +187,6 @@ static __init struct pci_dev *gx_detect_chipset(void) { struct pci_dev *gx_pci = NULL; - /* check if CPU is a MediaGX or a Geode. */ - if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) && - (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) { - pr_debug("error: no MediaGX/Geode processor found!\n"); - return NULL; - } - /* detect which companion chip is used */ for_each_pci_dev(gx_pci) { if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL) diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index f47d26e..53ddbc7 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -35,6 +35,7 @@ #include #include +#include #include #include "longhaul.h" @@ -951,12 +952,17 @@ static struct cpufreq_driver longhaul_driver = { .attr = longhaul_attr, }; +static const struct x86_cpu_id longhaul_id[] = { + { X86_VENDOR_CENTAUR, 6 }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, longhaul_id); static int __init longhaul_init(void) { struct cpuinfo_x86 *c = &cpu_data(0); - if (c->x86_vendor != X86_VENDOR_CENTAUR || c->x86 != 6) + if (!x86_match_cpu(longhaul_id)) return -ENODEV; #ifdef CONFIG_SMP diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c index 34ea359..8bc9f5f 100644 --- a/drivers/cpufreq/longrun.c +++ b/drivers/cpufreq/longrun.c @@ -14,6 +14,7 @@ #include #include +#include static struct cpufreq_driver longrun_driver; @@ -288,6 +289,12 @@ static struct cpufreq_driver longrun_driver = { .owner = THIS_MODULE, }; +static const struct x86_cpu_id longrun_ids[] = { + { X86_VENDOR_TRANSMETA, X86_FAMILY_ANY, X86_MODEL_ANY, + X86_FEATURE_LONGRUN }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, longrun_ids); /** * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver @@ -296,12 +303,8 @@ static struct cpufreq_driver longrun_driver = { */ static int __init longrun_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); - - if (c->x86_vendor != X86_VENDOR_TRANSMETA || - !cpu_has(c, X86_FEATURE_LONGRUN)) + if (!x86_match_cpu(longrun_ids)) return -ENODEV; - return cpufreq_register_driver(&longrun_driver); } diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 6be3e07..827629c9 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "speedstep-lib.h" @@ -289,21 +290,25 @@ static struct cpufreq_driver p4clockmod_driver = { .attr = p4clockmod_attr, }; +static const struct x86_cpu_id cpufreq_p4_id[] = { + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_ACC }, + {} +}; + +/* + * Intentionally no MODULE_DEVICE_TABLE here: this driver should not + * be auto loaded. Please don't add one. + */ static int __init cpufreq_p4_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); int ret; /* * THERM_CONTROL is architectural for IA32 now, so * we can rely on the capability checks */ - if (c->x86_vendor != X86_VENDOR_INTEL) - return -ENODEV; - - if (!test_cpu_cap(c, X86_FEATURE_ACPI) || - !test_cpu_cap(c, X86_FEATURE_ACC)) + if (!x86_match_cpu(cpufreq_p4_id) || !boot_cpu_has(X86_FEATURE_ACPI)) return -ENODEV; ret = cpufreq_register_driver(&p4clockmod_driver); diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index b3379d6..54dd031 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -16,6 +16,7 @@ #include #include +#include #include #define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long @@ -210,6 +211,12 @@ static struct cpufreq_driver powernow_k6_driver = { .attr = powernow_k6_attr, }; +static const struct x86_cpu_id powernow_k6_ids[] = { + { X86_VENDOR_AMD, 5, 12 }, + { X86_VENDOR_AMD, 5, 13 }, + {} +}; + /** * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver @@ -220,10 +227,7 @@ static struct cpufreq_driver powernow_k6_driver = { */ static int __init powernow_k6_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); - - if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) || - ((c->x86_model != 12) && (c->x86_model != 13))) + if (!x86_match_cpu(powernow_k6_ids)) return -ENODEV; if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) { diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index d71d9f3..501d167 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -28,6 +28,7 @@ #include /* Needed for recalibrate_cpu_khz() */ #include #include +#include #ifdef CONFIG_X86_POWERNOW_K7_ACPI #include @@ -110,18 +111,19 @@ static int check_fsb(unsigned int fsbspeed) return delta < 5; } +static const struct x86_cpu_id powernow_k7_cpuids[] = { + { X86_VENDOR_AMD, 7, }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids); + static int check_powernow(void) { struct cpuinfo_x86 *c = &cpu_data(0); unsigned int maxei, eax, ebx, ecx, edx; - if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 6)) { -#ifdef MODULE - printk(KERN_INFO PFX "This module only works with " - "AMD K7 CPUs\n"); -#endif + if (!x86_match_cpu(powernow_k7_cpuids)) return 0; - } /* Get maximum capabilities */ maxei = cpuid_eax(0x80000000); diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 8f9b2cee..c0e8164 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -520,6 +521,15 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, return 0; } +static const struct x86_cpu_id powernow_k8_ids[] = { + /* IO based frequency switching */ + { X86_VENDOR_AMD, 0xf }, + /* MSR based frequency switching supported */ + X86_FEATURE_MATCH(X86_FEATURE_HW_PSTATE), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, powernow_k8_ids); + static void check_supported_cpu(void *_rc) { u32 eax, ebx, ecx, edx; @@ -527,13 +537,7 @@ static void check_supported_cpu(void *_rc) *rc = -ENODEV; - if (__this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_AMD) - return; - eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE); - if (((eax & CPUID_XFAM) != CPUID_XFAM_K8) && - ((eax & CPUID_XFAM) < CPUID_XFAM_10H)) - return; if ((eax & CPUID_XFAM) == CPUID_XFAM_K8) { if (((eax & CPUID_USE_XFAM_XMOD) != CPUID_USE_XFAM_XMOD) || @@ -1553,6 +1557,9 @@ static int __cpuinit powernowk8_init(void) unsigned int i, supported_cpus = 0, cpu; int rv; + if (!x86_match_cpu(powernow_k8_ids)) + return -ENODEV; + for_each_online_cpu(i) { int rc; smp_call_function_single(i, check_supported_cpu, &rc, 1); diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index 1e205e6..e42e073 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -22,6 +22,7 @@ #include #include +#include #include #define MMCR_BASE 0xfffef000 /* The default base address */ @@ -150,18 +151,19 @@ static struct cpufreq_driver sc520_freq_driver = { .attr = sc520_freq_attr, }; +static const struct x86_cpu_id sc520_ids[] = { + { X86_VENDOR_AMD, 4, 9 }, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, sc520_ids); static int __init sc520_freq_init(void) { - struct cpuinfo_x86 *c = &cpu_data(0); int err; - /* Test if we have the right hardware */ - if (c->x86_vendor != X86_VENDOR_AMD || - c->x86 != 4 || c->x86_model != 9) { - pr_debug("no Elan SC520 processor found!\n"); + if (!x86_match_cpu(sc520_ids)) return -ENODEV; - } + cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1); if (!cpuctl) { printk(KERN_ERR "sc520_freq: error: failed to remap memory\n"); diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 6ea3455..3a953d5 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -25,6 +25,7 @@ #include #include #include +#include #define PFX "speedstep-centrino: " #define MAINTAINER "cpufreq@vger.kernel.org" @@ -595,6 +596,24 @@ static struct cpufreq_driver centrino_driver = { .owner = THIS_MODULE, }; +/* + * This doesn't replace the detailed checks above because + * the generic CPU IDs don't have a way to match for steppings + * or ASCII model IDs. + */ +static const struct x86_cpu_id centrino_ids[] = { + { X86_VENDOR_INTEL, 6, 9, X86_FEATURE_EST }, + { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST }, + { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST }, + { X86_VENDOR_INTEL, 6, 13, X86_FEATURE_EST }, + { X86_VENDOR_INTEL, 15, 3, X86_FEATURE_EST }, + { X86_VENDOR_INTEL, 15, 4, X86_FEATURE_EST }, + {} +}; +#if 0 +/* Autoload or not? Do not for now. */ +MODULE_DEVICE_TABLE(x86cpu, centrino_ids); +#endif /** * centrino_init - initializes the Enhanced SpeedStep CPUFreq driver @@ -612,11 +631,8 @@ static struct cpufreq_driver centrino_driver = { */ static int __init centrino_init(void) { - struct cpuinfo_x86 *cpu = &cpu_data(0); - - if (!cpu_has(cpu, X86_FEATURE_EST)) + if (!x86_match_cpu(centrino_ids)) return -ENODEV; - return cpufreq_register_driver(¢rino_driver); } diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index a748ce7..7432b3a 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -25,6 +25,8 @@ #include #include +#include + #include "speedstep-lib.h" @@ -388,6 +390,16 @@ static struct cpufreq_driver speedstep_driver = { .attr = speedstep_attr, }; +static const struct x86_cpu_id ss_smi_ids[] = { + { X86_VENDOR_INTEL, 6, 0xb, }, + { X86_VENDOR_INTEL, 6, 0x8, }, + { X86_VENDOR_INTEL, 15, 2 }, + {} +}; +#if 0 +/* Autoload or not? Do not for now. */ +MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids); +#endif /** * speedstep_init - initializes the SpeedStep CPUFreq driver @@ -398,6 +410,9 @@ static struct cpufreq_driver speedstep_driver = { */ static int __init speedstep_init(void) { + if (!x86_match_cpu(ss_smi_ids)) + return -ENODEV; + /* detect processor */ speedstep_processor = speedstep_detect_processor(); if (!speedstep_processor) { diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index 8af2d2f..7047821 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -249,6 +249,7 @@ EXPORT_SYMBOL_GPL(speedstep_get_frequency); * DETECT SPEEDSTEP-CAPABLE PROCESSOR * *********************************************************************/ +/* Keep in sync with the x86_cpu_id tables in the different modules */ unsigned int speedstep_detect_processor(void) { struct cpuinfo_x86 *c = &cpu_data(0); diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index c76ead3..6a457fc 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "speedstep-lib.h" @@ -379,6 +380,17 @@ static struct cpufreq_driver speedstep_driver = { .attr = speedstep_attr, }; +static const struct x86_cpu_id ss_smi_ids[] = { + { X86_VENDOR_INTEL, 6, 0xb, }, + { X86_VENDOR_INTEL, 6, 0x8, }, + { X86_VENDOR_INTEL, 15, 2 }, + {} +}; +#if 0 +/* Not auto loaded currently */ +MODULE_DEVICE_TABLE(x86cpu, ss_smi_ids); +#endif + /** * speedstep_init - initializes the SpeedStep CPUFreq driver * @@ -388,6 +400,9 @@ static struct cpufreq_driver speedstep_driver = { */ static int __init speedstep_init(void) { + if (!x86_match_cpu(ss_smi_ids)) + return -ENODEV; + speedstep_processor = speedstep_detect_processor(); switch (speedstep_processor) { -- cgit v1.1 From 78ff123b05fb15beb1ad670372eea0d299d0b8af Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 26 Jan 2012 00:09:13 +0100 Subject: x86: autoload microcode driver on Intel and AMD systems v2 Don't try to describe the actual models for now. v2: Fix typo: X86_VENDOR_ANY -> X86_FAMILY_ANY (trenn) Signed-off-by: Andi Kleen Signed-off-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/microcode_core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index fda91c3..87a0f86 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c @@ -86,6 +86,7 @@ #include #include +#include MODULE_DESCRIPTION("Microcode Update Driver"); MODULE_AUTHOR("Tigran Aivazian "); @@ -504,6 +505,20 @@ static struct notifier_block __refdata mc_cpu_notifier = { .notifier_call = mc_cpu_callback, }; +#ifdef MODULE +/* Autoload on Intel and AMD systems */ +static const struct x86_cpu_id microcode_id[] = { +#ifdef CONFIG_MICROCODE_INTEL + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, }, +#endif +#ifdef CONFIG_MICROCODE_AMD + { X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, }, +#endif + {} +}; +MODULE_DEVICE_TABLE(x86cpu, microcode_id); +#endif + static int __init microcode_init(void) { struct cpuinfo_x86 *c = &cpu_data(0); -- cgit v1.1 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(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 864cc6e..6baa1e6 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 7acc961..940e2d4 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 7c89880..a524353 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 db87e78..2a0c670 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 1f65875..6e53b48 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.1 From ed283e9f0a2cc0541870828c76c6c6997c51a318 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 24 Jan 2012 14:35:13 -0500 Subject: USB/PCI/PCMCIA: Clean up new_id and remove_id sysfs attribute routines This patch (as1514) cleans up some places where new_id and remove_id sysfs attributes are created and deleted. Handling both attributes in a single routine rather than a pair of routines makes the code smaller. It also prevents certain kinds of errors, like one we currently have in the USB subsystem: The removeid attribute is often created even when newid isn't (because the driver's no_dynamid_id flag is set). In the case of the PCMCIA subsystem, the newid attribute is created but never explicitly deleted. The patch adds a deletion routine. Signed-off-by: Alan Stern Acked-by: Jesse Barnes Acked-by: Dominik Brodowski Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-driver.c | 50 +++++++++++++------------------------- drivers/pcmcia/ds.c | 6 +++++ drivers/usb/core/driver.c | 61 ++++++++++++++++------------------------------- 3 files changed, 43 insertions(+), 74 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ff54047..8d9616b 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -188,43 +188,34 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); static int -pci_create_newid_file(struct pci_driver *drv) +pci_create_newid_files(struct pci_driver *drv) { int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->driver, &driver_attr_new_id); - return error; -} - -static void pci_remove_newid_file(struct pci_driver *drv) -{ - driver_remove_file(&drv->driver, &driver_attr_new_id); -} -static int -pci_create_removeid_file(struct pci_driver *drv) -{ - int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->driver,&driver_attr_remove_id); + if (drv->probe != NULL) { + error = driver_create_file(&drv->driver, &driver_attr_new_id); + if (error == 0) { + error = driver_create_file(&drv->driver, + &driver_attr_remove_id); + if (error) + driver_remove_file(&drv->driver, + &driver_attr_new_id); + } + } return error; } -static void pci_remove_removeid_file(struct pci_driver *drv) +static void pci_remove_newid_files(struct pci_driver *drv) { driver_remove_file(&drv->driver, &driver_attr_remove_id); + driver_remove_file(&drv->driver, &driver_attr_new_id); } #else /* !CONFIG_HOTPLUG */ -static inline int pci_create_newid_file(struct pci_driver *drv) +static inline int pci_create_newid_files(struct pci_driver *drv) { return 0; } -static inline void pci_remove_newid_file(struct pci_driver *drv) {} -static inline int pci_create_removeid_file(struct pci_driver *drv) -{ - return 0; -} -static inline void pci_remove_removeid_file(struct pci_driver *drv) {} +static inline void pci_remove_newid_files(struct pci_driver *drv) {} #endif /** @@ -1136,18 +1127,12 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, if (error) goto out; - error = pci_create_newid_file(drv); + error = pci_create_newid_files(drv); if (error) goto out_newid; - - error = pci_create_removeid_file(drv); - if (error) - goto out_removeid; out: return error; -out_removeid: - pci_remove_newid_file(drv); out_newid: driver_unregister(&drv->driver); goto out; @@ -1166,8 +1151,7 @@ out_newid: void pci_unregister_driver(struct pci_driver *drv) { - pci_remove_removeid_file(drv); - pci_remove_newid_file(drv); + pci_remove_newid_files(drv); driver_unregister(&drv->driver); pci_free_dynids(drv); } diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 059699f..249b889 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -157,6 +157,11 @@ pcmcia_create_newid_file(struct pcmcia_driver *drv) return error; } +static void +pcmcia_remove_newid_file(struct pcmcia_driver *drv) +{ + driver_remove_file(&drv->drv, &driver_attr_new_id); +} /** * pcmcia_register_driver - register a PCMCIA driver with the bus core @@ -201,6 +206,7 @@ EXPORT_SYMBOL(pcmcia_register_driver); void pcmcia_unregister_driver(struct pcmcia_driver *driver) { pr_debug("unregistering driver %s\n", driver->name); + pcmcia_remove_newid_file(driver); driver_unregister(&driver->drv); pcmcia_free_dynids(driver); } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 54c493b..4fee024 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -129,43 +129,39 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); -static int usb_create_newid_file(struct usb_driver *usb_drv) +static int usb_create_newid_files(struct usb_driver *usb_drv) { int error = 0; if (usb_drv->no_dynamic_id) goto exit; - if (usb_drv->probe != NULL) + if (usb_drv->probe != NULL) { error = driver_create_file(&usb_drv->drvwrap.driver, &driver_attr_new_id); + if (error == 0) { + error = driver_create_file(&usb_drv->drvwrap.driver, + &driver_attr_remove_id); + if (error) + driver_remove_file(&usb_drv->drvwrap.driver, + &driver_attr_new_id); + } + } exit: return error; } -static void usb_remove_newid_file(struct usb_driver *usb_drv) +static void usb_remove_newid_files(struct usb_driver *usb_drv) { if (usb_drv->no_dynamic_id) return; - if (usb_drv->probe != NULL) + if (usb_drv->probe != NULL) { driver_remove_file(&usb_drv->drvwrap.driver, - &driver_attr_new_id); -} - -static int -usb_create_removeid_file(struct usb_driver *drv) -{ - int error = 0; - if (drv->probe != NULL) - error = driver_create_file(&drv->drvwrap.driver, &driver_attr_remove_id); - return error; -} - -static void usb_remove_removeid_file(struct usb_driver *drv) -{ - driver_remove_file(&drv->drvwrap.driver, &driver_attr_remove_id); + driver_remove_file(&usb_drv->drvwrap.driver, + &driver_attr_new_id); + } } static void usb_free_dynids(struct usb_driver *usb_drv) @@ -180,22 +176,12 @@ static void usb_free_dynids(struct usb_driver *usb_drv) spin_unlock(&usb_drv->dynids.lock); } #else -static inline int usb_create_newid_file(struct usb_driver *usb_drv) -{ - return 0; -} - -static void usb_remove_newid_file(struct usb_driver *usb_drv) -{ -} - -static int -usb_create_removeid_file(struct usb_driver *drv) +static inline int usb_create_newid_files(struct usb_driver *usb_drv) { return 0; } -static void usb_remove_removeid_file(struct usb_driver *drv) +static void usb_remove_newid_files(struct usb_driver *usb_drv) { } @@ -872,22 +858,16 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, usbfs_update_special(); - retval = usb_create_newid_file(new_driver); + retval = usb_create_newid_files(new_driver); if (retval) goto out_newid; - retval = usb_create_removeid_file(new_driver); - if (retval) - goto out_removeid; - pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name); out: return retval; -out_removeid: - usb_remove_newid_file(new_driver); out_newid: driver_unregister(&new_driver->drvwrap.driver); @@ -914,10 +894,9 @@ void usb_deregister(struct usb_driver *driver) pr_info("%s: deregistering interface driver %s\n", usbcore_name, driver->name); - usb_remove_removeid_file(driver); - usb_remove_newid_file(driver); - usb_free_dynids(driver); + usb_remove_newid_files(driver); driver_unregister(&driver->drvwrap.driver); + usb_free_dynids(driver); usbfs_update_special(); } -- cgit v1.1 From 07d251460bbf9752c6532af8c1a68328c199dd70 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 27 Jan 2012 10:24:40 -0500 Subject: PCI/XEN: Fix bug introduced by a recent change This patch (as1516) fixes a bug introduced during the removal of put_driver() and get_driver() from drivers/pci/xen-pcifront.c. Reported-by: Stephen Rothwell Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/pci/xen-pcifront.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 6f81998..98387ca 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -593,7 +593,7 @@ static pci_ers_result_t pcifront_common_process(int cmd, } pdrv = pcidev->driver; - if (pdrv->driver) { + if (pdrv) { if (pdrv->err_handler && pdrv->err_handler->error_detected) { dev_dbg(&pcidev->dev, "trying to call AER service\n"); -- cgit v1.1 From d5c38b137ac8a6e3dbed13bc494d60df5b69dfc4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 31 Jan 2012 06:40:26 -0800 Subject: sysfs: Update the name hash when renaming sysfs entries This fixes a bug introduced with sysfs name hashes where renaming a network device appears to succeed but silently makes the sysfs files for that network device inaccessible. In at least one configuration this bug has stopped networking from coming up during boot. Signed-off-by: Eric W. Biederman Tested-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index ea64d01..dd3779c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -872,6 +872,7 @@ int sysfs_rename(struct sysfs_dirent *sd, dup_name = sd->s_name; sd->s_name = new_name; + sd->s_hash = sysfs_name_hash(sd->s_ns, sd->s_name); } /* Move to the appropriate place in the appropriate directories rbtree. */ -- cgit v1.1 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(-) diff --git a/drivers/hv/hv_kvp.h b/drivers/hv/hv_kvp.h index 9b765d7..c2c5bba 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 3c9c54f..7638407 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 11224ed..2b6a2d9 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.1 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 diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0e8343f..4a6971e 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 c2c5bba..0000000 --- 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 55d58f2..dbb8b8e 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 62b908e..7332b3f 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.1 From b3012e12cdc5825f93367cc6294419e7ab1f00cc Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Jan 2012 14:47:28 +0000 Subject: cpufreq/gx: Fix the compile error Someone forgot to test this one it seems. Signed-off-by: Alan Cox Acked-by: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/gx-suspmod.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 5a06c0b..456bee0 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -172,7 +172,7 @@ static struct pci_device_id gx_chipset_tbl[] __initdata = { { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), }, { 0, }, }; -MODULE_DEVICE_TABLE(gx_chipset_tbl); +MODULE_DEVICE_TABLE(pci, gx_chipset_tbl); static void gx_write_byte(int reg, int value) { -- cgit v1.1 From 976a0be03a15f1c268e3f569c0ade3e7ff8ce478 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Tue, 7 Feb 2012 11:45:57 -0800 Subject: ACPI: remove duplicated lines of merging problems with acpi_processor_start When checking driver-core tree, found crazying warnings on my setups. [ 216.025849] calling acpi_processor_init+0x0/0x81 @ 1 [ 216.045332] ACPI: Requesting acpi_cpufreq [ 216.047454] Monitor-Mwait will be used to enter C-1 state [ 216.047912] Monitor-Mwait will be used to enter C-3 state [ 216.065270] ACPI: acpi_idle registered with cpuidle [ 216.068241] kobject (ffff8870364a1940): tried to init an initialized object, something is seriously wrong. [ 216.085287] Pid: 1, comm: swapper/0 Not tainted 3.3.0-rc2-tip-yh-02428-ge663840-dirty #247 [ 216.105041] Call Trace: [ 216.105192] [] kobject_init+0x33/0x83 [ 216.124880] [] kobject_init_and_add+0x23/0x57 [ 216.125158] [] cpuidle_add_sysfs+0x49/0x62 [ 216.144850] [] __cpuidle_register_device+0xe6/0x10e [ 216.145182] [] cpuidle_register_device+0x25/0x4d [ 216.164912] [] acpi_processor_power_init+0x13e/0x16c [ 216.165205] [] ? acpi_processor_get_throttling_info+0x128/0x158 [ 216.185012] [] acpi_processor_start+0x62/0x11d [ 216.204861] [] acpi_processor_add+0x1b0/0x1e7 [ 216.205144] [] acpi_device_probe+0x4e/0x11c [ 216.225063] [] really_probe+0x99/0x126 [ 216.225328] [] driver_probe_device+0x3b/0x56 [ 216.244846] [] __driver_attach+0x5f/0x82 [ 216.245101] [] ? driver_probe_device+0x56/0x56 [ 216.264668] [] bus_for_each_dev+0x5c/0x88 [ 216.264942] [] driver_attach+0x1e/0x20 [ 216.284639] [] bus_add_driver+0xca/0x21d [ 216.284903] [] ? local_clock+0xf/0x3c [ 216.304580] [] ? acpi_fan_init+0x18/0x18 [ 216.304849] [] driver_register+0x91/0xfe [ 216.324545] [] ? acpi_fan_init+0x18/0x18 [ 216.324813] [] acpi_bus_register_driver+0x43/0x45 [ 216.344563] [] acpi_processor_init+0x30/0x81 [ 216.344845] [] ? acpi_fan_init+0x18/0x18 [ 216.364590] [] do_one_initcall+0x57/0x134 [ 216.364868] [] kernel_init+0x146/0x1c0 [ 216.384512] [] kernel_thread_helper+0x4/0x10 [ 216.384819] [] ? retint_restore_args+0xe/0xe [ 216.404578] [] ? start_kernel+0x3ab/0x3ab [ 216.424530] [] ? gs_change+0xb/0xb [ 216.424793] ------------[ cut here ]------------ [ 216.425038] WARNING: at fs/sysfs/dir.c:502 sysfs_add_one+0x97/0xab() [ 216.444480] Hardware name: Sun Fire X4800 [ 216.444668] sysfs: cannot create duplicate filename '/devices/system/cpu/cpu0/cpuidle' ... It turns out acpi_processor_power_init() get called two time in acpi_processor_add and acpi_processor_start. Found several lines are duplicated in those two functions even related commit move them. The related patches are ok. Not sure how it could happen, looks like git problem. -v2: add back acpi_processor_load_module(pr) to acpi_processor_load_start Signed-off-by: Yinghai Lu Cc: Linus Torvalds Acked-by: Thomas Renninger Cc: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/processor_driver.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index f289d2a..2801b41 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -474,6 +474,7 @@ static __ref int acpi_processor_start(struct acpi_processor *pr) #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr, 0); + acpi_processor_load_module(pr); #endif acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); @@ -579,23 +580,6 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) goto err_free_cpumask; } -#ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr, 0); - acpi_processor_load_module(pr); -#endif - acpi_processor_get_throttling_info(pr); - acpi_processor_get_limit_info(pr); - - if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) - acpi_processor_power_init(pr, device); - - pr->cdev = thermal_cooling_device_register("Processor", device, - &processor_cooling_ops); - if (IS_ERR(pr->cdev)) { - result = PTR_ERR(pr->cdev); - goto err_remove_sysfs; - } - /* * Do not start hotplugged CPUs now, but when they * are onlined the first time -- cgit v1.1 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(-) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 7332b3f..b822978 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.1 From eab6af71f0b83a7f62b9c48be5b2c0a82a86fad3 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:49 -0800 Subject: tools: hv: Use hyperv.h to get the KVP definitions Now use hyperv.h to get the KVP defines in the KVP user-mode code. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 2b6a2d9..b75523c 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -34,17 +34,12 @@ #include #include #include +#include #include #include #include #include -/* - * - * 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. - */ /* * KVP protocol: The user mode component first registers with the @@ -56,25 +51,8 @@ * We use this infrastructure for also supporting queries from user mode * application for state that may be maintained in the KVP kernel component. * - * XXXKYS: Have a shared header file between the user and kernel (TODO) */ -enum kvp_op { - KVP_REGISTER = 0, /* Register the user mode component*/ - KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ - KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ - KVP_USER_GET, /*User is requesting the value for the specified key*/ - KVP_USER_SET /*User is providing the value for the specified key*/ -}; - -#define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 -#define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 - -struct hv_ku_msg { - __u32 kvp_index; - __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ - __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ -}; enum key_index { FullyQualifiedDomainName = 0, @@ -89,10 +67,6 @@ enum key_index { ProcessorArchitecture }; -/* - * End of shared definitions. - */ - static char kvp_send_buffer[4096]; static char kvp_recv_buffer[4096]; static struct sockaddr_nl addr; -- cgit v1.1 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(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 4a6971e..0ef4c1f 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 b822978..75aee67 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 b75523c..4ebf703 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.1 From 14c1bf8a8920f36f6e0603a2ff920b48eec14387 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 2 Feb 2012 16:56:51 -0800 Subject: drivers: hv: Increase the number of VCPUs supported in the guest The current code arbirarily limited the number of CPUs the guest could have. Change that so that we can support the maximum number of CPUs the guest can support. While we use NR_CPUS to size the per-cpu state all we are allocating based on NR_CPUS are the pointers to per-cpu state that will be allocatted in the context of the initializing CPU. This patch triggers a checkpatch warning for the usage of NR_CPU and since all we are allocating a couple of pointers per CPU, it should be ok. Signed-off-by: K. Y. Srinivasan Signed-off-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 4 ++-- drivers/hv/hyperv_vmbus.h | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 12aa97f..15956bd 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -155,9 +155,9 @@ int hv_init(void) union hv_x64_msr_hypercall_contents hypercall_msr; void *virtaddr = NULL; - memset(hv_context.synic_event_page, 0, sizeof(void *) * MAX_NUM_CPUS); + memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, - sizeof(void *) * MAX_NUM_CPUS); + sizeof(void *) * NR_CPUS); if (!query_hypervisor_presence()) goto cleanup; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 6d7d286..699f0d8 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -457,7 +457,6 @@ static const uuid_le VMBUS_SERVICE_ID = { }, }; -#define MAX_NUM_CPUS 32 struct hv_input_signal_event_buffer { @@ -483,8 +482,8 @@ struct hv_context { /* 8-bytes aligned of the buffer above */ struct hv_input_signal_event *signal_event_param; - void *synic_message_page[MAX_NUM_CPUS]; - void *synic_event_page[MAX_NUM_CPUS]; + void *synic_message_page[NR_CPUS]; + void *synic_event_page[NR_CPUS]; }; extern struct hv_context hv_context; -- cgit v1.1 From e250b34e57888ebe829a0b89cfa8ad303ad5ae74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 4 Feb 2012 16:33:55 +0000 Subject: w1: Use linux/gpio.h rather than asm/gpio.h Direct inclusion of the asm header has long been deprecated by the introduction of gpiolib. Signed-off-by: Mark Brown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/w1-gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index fcbe742..df600d1 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -13,12 +13,11 @@ #include #include #include +#include #include "../w1.h" #include "../w1_int.h" -#include - static void w1_gpio_write_bit_dir(void *data, u8 bit) { struct w1_gpio_platform_data *pdata = data; -- cgit v1.1 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(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index a16b1df..d4afd70 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.1 From 956563362be8ac7ce084b00825168be1adfb29ee Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 18 Nov 2011 10:14:24 -0800 Subject: DWC3: use module_pci_driver This cuts down on the boilerplate code. Cc: Lars-Peter Clausen Signed-off-by: Greg Kroah-Hartman Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-pci.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 64e1f7c..c68e427 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -171,14 +171,4 @@ MODULE_AUTHOR("Felipe Balbi "); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); -static int __devinit dwc3_pci_init(void) -{ - return pci_register_driver(&dwc3_pci_driver); -} -module_init(dwc3_pci_init); - -static void __exit dwc3_pci_exit(void) -{ - pci_unregister_driver(&dwc3_pci_driver); -} -module_exit(dwc3_pci_exit); +module_pci_driver(dwc3_pci_driver); -- cgit v1.1 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 diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 7be9f79..9aa618a 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 610f999..b6d1b9c 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 0000000..05f1503 --- /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 0000000..2739ccb --- /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.1 From da5a70f3519fd6f73ece3eea261a861c9a4d6bbd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 6 Feb 2012 11:22:23 -0800 Subject: Documentation: add information for new sysfs soc bus functionality This change applies the required documentation for each new attribute recenty added by the new System-On-Chip (SoC) information export bus driver. Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-devices-soc | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-soc diff --git a/Documentation/ABI/testing/sysfs-devices-soc b/Documentation/ABI/testing/sysfs-devices-soc new file mode 100644 index 0000000..6d9cc25 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-soc @@ -0,0 +1,58 @@ +What: /sys/devices/socX +Date: January 2012 +contact: Lee Jones +Description: + The /sys/devices/ directory contains a sub-directory for each + System-on-Chip (SoC) device on a running platform. Information + regarding each SoC can be obtained by reading sysfs files. This + functionality is only available if implemented by the platform. + + The directory created for each SoC will also house information + about devices which are commonly contained in /sys/devices/platform. + It has been agreed that if an SoC device exists, its supported + devices would be better suited to appear as children of that SoC. + +What: /sys/devices/socX/machine +Date: January 2012 +contact: Lee Jones +Description: + Read-only attribute common to all SoCs. Contains the SoC machine + name (e.g. Ux500). + +What: /sys/devices/socX/family +Date: January 2012 +contact: Lee Jones +Description: + Read-only attribute common to all SoCs. Contains SoC family name + (e.g. DB8500). + +What: /sys/devices/socX/soc_id +Date: January 2012 +contact: Lee Jones +Description: + Read-only attribute supported by most SoCs. In the case of + ST-Ericsson's chips this contains the SoC serial number. + +What: /sys/devices/socX/revision +Date: January 2012 +contact: Lee Jones +Description: + Read-only attribute supported by most SoCs. Contains the SoC's + manufacturing revision number. + +What: /sys/devices/socX/process +Date: January 2012 +contact: Lee Jones +Description: + Read-only attribute supported ST-Ericsson's silicon. Contains the + the process by which the silicon chip was manufactured. + +What: /sys/bus/soc +Date: January 2012 +contact: Lee Jones +Description: + The /sys/bus/soc/ directory contains the usual sub-folders + expected under most buses. /sys/bus/soc/devices is of particular + interest, as it contains a symlink for each SoC device found on + the system. Each symlink points back into the aforementioned + /sys/devices/socX devices. -- cgit v1.1 From 70142a9dd154f54f7409871ead86f7d77f2c6576 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Feb 2012 22:56:59 +0000 Subject: x86/cpu: Fix overrun check in arch_print_cpu_modalias() snprintf() does not return a negative value when truncating. Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c index 940e2d4..2dfa52b 100644 --- a/arch/x86/kernel/cpu/match.c +++ b/arch/x86/kernel/cpu/match.c @@ -67,7 +67,7 @@ ssize_t arch_print_cpu_modalias(struct device *dev, for (i = 0; i < NCAPINTS*32; i++) { if (boot_cpu_has(i)) { n = snprintf(buf, size, ",%04X", i); - if (n < 0) { + if (n >= size) { WARN(1, "x86 features overflow page\n"); break; } -- cgit v1.1 From 5467bdda4a326513c2f14b712a22d59115b7ae94 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Feb 2012 22:57:19 +0000 Subject: x86/cpu: Clean up modalias feature matching We currently include commas on both sides of the feature ID in a modalias, but this prevents the lowest numbered feature of a CPU from being matched. Since all feature IDs have the same length, we do not need to worry about substring matches, so omit commas from the modalias entirely. Avoid generating multiple adjacent wildcards when there is no feature ID to match. Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/match.c | 3 +-- scripts/mod/file2alias.c | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/match.c b/arch/x86/kernel/cpu/match.c index 2dfa52b..5502b28 100644 --- a/arch/x86/kernel/cpu/match.c +++ b/arch/x86/kernel/cpu/match.c @@ -63,7 +63,7 @@ ssize_t arch_print_cpu_modalias(struct device *dev, boot_cpu_data.x86_model); size -= n; buf += n; - size -= 2; + size -= 1; for (i = 0; i < NCAPINTS*32; i++) { if (boot_cpu_has(i)) { n = snprintf(buf, size, ",%04X", i); @@ -75,7 +75,6 @@ ssize_t arch_print_cpu_modalias(struct device *dev, buf += n; } } - *buf++ = ','; *buf++ = '\n'; return buf - bufptr; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index a468af0..78fd81f 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1021,8 +1021,9 @@ static int do_x86cpu_entry(const char *filename, struct x86_cpu_id *id, 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, ",*"); + strcat(alias, ":feature:*"); + if (id->feature != X86_FEATURE_ANY) + sprintf(alias + strlen(alias), "%04X*", id->feature); return 1; } ADD_TO_DEVTABLE("x86cpu", struct x86_cpu_id, do_x86cpu_entry); -- cgit v1.1 From e668505c9811411c6096888b43ba104f35c9e9c3 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Feb 2012 22:57:38 +0000 Subject: intel_idle: Fix ID for Nehalem-EX Xeon in device ID table Commit b66b8b9a4a79087dde1b358a016e5c8739ccf186 ('intel-idle: convert to x86_cpu_id auto probing') put two entries for model 0x2f (Westmere-EX Xeon) in the device ID table and left out model 0x2e (Nehalem-EX Xeon). Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/idle/intel_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 237fe57..a238649 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -360,7 +360,7 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x1f, idle_cpu_nehalem), ICPU(0x25, idle_cpu_westmere), ICPU(0x2c, idle_cpu_westmere), - ICPU(0x2f, idle_cpu_westmere), + ICPU(0x2e, idle_cpu_westmere), ICPU(0x1c, idle_cpu_atom), ICPU(0x26, idle_cpu_lincroft), ICPU(0x2f, idle_cpu_westmere), -- cgit v1.1 From 30bcfff9bd41db5edab6420d0ae2e435609eb083 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Feb 2012 22:58:14 +0000 Subject: powernow-k7: Fix CPU family number Commit fa8031aefec0cf7ea6c2387c93610d99d9659aa2 ('cpufreq: Add support for x86 cpuinfo auto loading v4') seems to have inadvertently changed the matched CPU family number from 6 to 7. Change it back. Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/powernow-k7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 501d167..cf7e1ee 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -112,7 +112,7 @@ static int check_fsb(unsigned int fsbspeed) } static const struct x86_cpu_id powernow_k7_cpuids[] = { - { X86_VENDOR_AMD, 7, }, + { X86_VENDOR_AMD, 6, }, {} }; MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids); -- cgit v1.1 From b4d2d23148b446f6853e711eb31c533c7385eba5 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 11 Feb 2012 23:04:12 +0000 Subject: powernow-k6: Really enable auto-loading Commit fa8031aefec0cf7ea6c2387c93610d99d9659aa2 ('cpufreq: Add support for x86 cpuinfo auto loading v4') added a device ID table to this driver, but didn't declare it as the module device ID table. Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/powernow-k6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index 54dd031..af23e0b 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -216,7 +216,7 @@ static const struct x86_cpu_id powernow_k6_ids[] = { { X86_VENDOR_AMD, 5, 13 }, {} }; - +MODULE_DEVICE_TABLE(x86cpu, powernow_k6_ids); /** * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver -- cgit v1.1 From bc5bca53cca350eb90fc9f84c2e37ba6383807c3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 15 Feb 2012 14:48:01 -0800 Subject: driver-core: documentation: fix up Greg's email address My old email address was used in a lot of documentation files, so fix this up to point to the correct one now. Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/removed/devfs | 2 +- Documentation/ABI/stable/sysfs-driver-usb-usbtmc | 10 +++++----- Documentation/ABI/testing/sysfs-class | 2 +- Documentation/ABI/testing/sysfs-devices | 2 +- Documentation/ABI/testing/sysfs-driver-samsung-laptop | 2 +- Documentation/ioctl/ioctl-number.txt | 2 +- Documentation/ko_KR/HOWTO | 2 +- Documentation/kobject.txt | 2 +- Documentation/zh_CN/HOWTO | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/removed/devfs b/Documentation/ABI/removed/devfs index 8ffd28b..0020c49 100644 --- a/Documentation/ABI/removed/devfs +++ b/Documentation/ABI/removed/devfs @@ -1,6 +1,6 @@ What: devfs Date: July 2005 (scheduled), finally removed in kernel v2.6.18 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: devfs has been unmaintained for a number of years, has unfixable races, contains a naming policy within the kernel that is diff --git a/Documentation/ABI/stable/sysfs-driver-usb-usbtmc b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc index 9a75fb2..23a43b8 100644 --- a/Documentation/ABI/stable/sysfs-driver-usb-usbtmc +++ b/Documentation/ABI/stable/sysfs-driver-usb-usbtmc @@ -1,7 +1,7 @@ What: /sys/bus/usb/drivers/usbtmc/devices/*/interface_capabilities What: /sys/bus/usb/drivers/usbtmc/devices/*/device_capabilities Date: August 2008 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: These files show the various USB TMC capabilities as described by the device itself. The full description of the bitfields @@ -15,7 +15,7 @@ Description: What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_interface_capabilities What: /sys/bus/usb/drivers/usbtmc/devices/*/usb488_device_capabilities Date: August 2008 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: These files show the various USB TMC capabilities as described by the device itself. The full description of the bitfields @@ -29,7 +29,7 @@ Description: What: /sys/bus/usb/drivers/usbtmc/devices/*/TermChar Date: August 2008 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: This file is the TermChar value to be sent to the USB TMC device as described by the document, "Universal Serial Bus Test @@ -42,7 +42,7 @@ Description: What: /sys/bus/usb/drivers/usbtmc/devices/*/TermCharEnabled Date: August 2008 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: This file determines if the TermChar is to be sent to the device on every transaction or not. For more details about @@ -53,7 +53,7 @@ Description: What: /sys/bus/usb/drivers/usbtmc/devices/*/auto_abort Date: August 2008 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: This file determines if the the transaction of the USB TMC device is to be automatically aborted if there is any error. diff --git a/Documentation/ABI/testing/sysfs-class b/Documentation/ABI/testing/sysfs-class index 4b0cb89..676530f 100644 --- a/Documentation/ABI/testing/sysfs-class +++ b/Documentation/ABI/testing/sysfs-class @@ -1,6 +1,6 @@ What: /sys/class/ Date: Febuary 2006 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: The /sys/class directory will consist of a group of subdirectories describing individual classes of devices diff --git a/Documentation/ABI/testing/sysfs-devices b/Documentation/ABI/testing/sysfs-devices index 6a25671..5fcc943 100644 --- a/Documentation/ABI/testing/sysfs-devices +++ b/Documentation/ABI/testing/sysfs-devices @@ -1,6 +1,6 @@ What: /sys/devices Date: February 2006 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: The /sys/devices tree contains a snapshot of the internal state of the kernel device tree. Devices will diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop index 0a81023..e82e7c2 100644 --- a/Documentation/ABI/testing/sysfs-driver-samsung-laptop +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -1,7 +1,7 @@ What: /sys/devices/platform/samsung/performance_level Date: January 1, 2010 KernelVersion: 2.6.33 -Contact: Greg Kroah-Hartman +Contact: Greg Kroah-Hartman Description: Some Samsung laptops have different "performance levels" that are can be modified by a function key, and by this sysfs file. These values don't always make a whole lot diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 4840334..5dbd906 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -189,7 +189,7 @@ Code Seq#(hex) Include File Comments 'Y' all linux/cyclades.h 'Z' 14-15 drivers/message/fusion/mptctl.h '[' 00-07 linux/usb/tmc.h USB Test and Measurement Devices - + 'a' all linux/atm*.h, linux/sonet.h ATM on linux 'b' 00-FF conflict! bit3 vme host bridge diff --git a/Documentation/ko_KR/HOWTO b/Documentation/ko_KR/HOWTO index ab5189a..2f48f20 100644 --- a/Documentation/ko_KR/HOWTO +++ b/Documentation/ko_KR/HOWTO @@ -354,7 +354,7 @@ Andrew Morton에 의해 배포된 실험적인 커널 패치들이다. Andrew는 git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git quilt trees: - - USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@suse.de> + - USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@linuxfoundation.org> kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ - x86-64, partly i386, Andi Kleen < ak@suse.de> ftp.firstfloor.org:/pub/ak/x86_64/quilt/ diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt index 3ab2472..49578cf 100644 --- a/Documentation/kobject.txt +++ b/Documentation/kobject.txt @@ -1,6 +1,6 @@ Everything you never wanted to know about kobjects, ksets, and ktypes -Greg Kroah-Hartman +Greg Kroah-Hartman Based on an original article by Jon Corbet for lwn.net written October 1, 2003 and located at http://lwn.net/Articles/51437/ diff --git a/Documentation/zh_CN/HOWTO b/Documentation/zh_CN/HOWTO index faf976c..7fba5aa 100644 --- a/Documentation/zh_CN/HOWTO +++ b/Documentation/zh_CN/HOWTO @@ -316,7 +316,7 @@ linux-kernel邮件列表中提供反馈,告诉大家你遇到了问题还是 git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git 使用quilt管理的补丁集: - - USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman + - USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ - x86-64, 部分i386, Andi Kleen ftp.firstfloor.org:/pub/ak/x86_64/quilt/ -- cgit v1.1 From 9f3519d2ed26d2ede5b6432fb64af0e7e8ed13e3 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 15 Feb 2012 18:21:42 +0100 Subject: w1: Fix w1_bq27000 w1_bq27000 adds a bq27000-battery platform device but does not provide platform data for it. This causes the bq27x00 driver to dereference a NULL pointer. So provide the appropriate platform data. This requires modifying w1_bq27000_read so that it find the w1 device as the parent of the bq device. Also there is no point exporting w1_bq27000_read as nothing else uses it or could use it. So make it static. Finally, as there is no way to track how many batteries have been found, and we will probably only find one, use an id number of '-1' to assert that this is a unique instance. Signed-off-by: NeilBrown Tested-by: Thomas Weber Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 8f4c91f..8f10fd2 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "../w1.h" #include "../w1_int.h" @@ -39,10 +40,10 @@ void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) } EXPORT_SYMBOL(w1_bq27000_write); -int w1_bq27000_read(struct device *dev, u8 reg) +static int w1_bq27000_read(struct device *dev, unsigned int reg) { u8 val; - struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); if (!dev) return 0; @@ -52,19 +53,25 @@ int w1_bq27000_read(struct device *dev, u8 reg) return val; } -EXPORT_SYMBOL(w1_bq27000_read); + +static struct bq27000_platform_data bq27000_battery_info = { + .read = w1_bq27000_read, + .name = "bq27000-battery", +}; static int w1_bq27000_add_slave(struct w1_slave *sl) { int ret; - int id = 1; struct platform_device *pdev; - pdev = platform_device_alloc("bq27000-battery", id); + pdev = platform_device_alloc("bq27000-battery", -1); if (!pdev) { ret = -ENOMEM; return ret; } + ret = platform_device_add_data(pdev, + &bq27000_battery_info, + sizeof(bq27000_battery_info)); pdev->dev.parent = &sl->dev; ret = platform_device_add(pdev); -- cgit v1.1 From 8bf11938459ff8ceb8643258d0a35e0f2bc9be17 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 16 Feb 2012 04:13:14 +0000 Subject: intel_idle: Revert change of auto_demotion_disable_flags for Nehalem Commit b66b8b9a4a79087dde1b358a016e5c8739ccf186 ('intel-idle: convert to x86_cpu_id auto probing') added a distinction between Nehalem and Westemere processors and changed auto_demotion_disable_flags for the former to 0. This was not explained in the commit message, so change it back. Signed-off-by: Ben Hutchings Acked-by: Thomas Renninger Acked-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- drivers/idle/intel_idle.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index a238649..1c15e9b 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -331,10 +331,6 @@ static void auto_demotion_disable(void *dummy) static const struct idle_cpu idle_cpu_nehalem = { .state_table = nehalem_cstates, -}; - -static const struct idle_cpu idle_cpu_westmere = { - .state_table = nehalem_cstates, .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, }; @@ -358,12 +354,12 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x1a, idle_cpu_nehalem), ICPU(0x1e, idle_cpu_nehalem), ICPU(0x1f, idle_cpu_nehalem), - ICPU(0x25, idle_cpu_westmere), - ICPU(0x2c, idle_cpu_westmere), - ICPU(0x2e, idle_cpu_westmere), + ICPU(0x25, idle_cpu_nehalem), + ICPU(0x2c, idle_cpu_nehalem), + ICPU(0x2e, idle_cpu_nehalem), ICPU(0x1c, idle_cpu_atom), ICPU(0x26, idle_cpu_lincroft), - ICPU(0x2f, idle_cpu_westmere), + ICPU(0x2f, idle_cpu_nehalem), ICPU(0x2a, idle_cpu_snb), ICPU(0x2d, idle_cpu_snb), {} -- cgit v1.1 From 93518dd2ebafcc761a8637b2877008cfd748c202 Mon Sep 17 00:00:00 2001 From: Masami Ichikawa Date: Tue, 21 Feb 2012 07:43:50 +0900 Subject: sysfs: Fix memory leak in sysfs_sd_setsecdata(). This patch fixies follwing two memory leak patterns that reported by kmemleak. sysfs_sd_setsecdata() is called during sys_lsetxattr() operation. It checks sd->s_iattr is NULL or not. Then if it is NULL, it calls sysfs_init_inode_attrs() to allocate memory. That code is this. iattrs = sd->s_iattr; if (!iattrs) iattrs = sysfs_init_inode_attrs(sd); The iattrs recieves sysfs_init_inode_attrs()'s result, but sd->s_iattr doesn't know the address. so it needs to set correct address to sd->s_iattr to free memory in other function. unreferenced object 0xffff880250b73e60 (size 32): comm "systemd", pid 1, jiffies 4294683888 (age 94.553s) hex dump (first 32 bytes): 73 79 73 74 65 6d 5f 75 3a 6f 62 6a 65 63 74 5f system_u:object_ 72 3a 73 79 73 66 73 5f 74 3a 73 30 00 00 00 00 r:sysfs_t:s0.... backtrace: [] kmemleak_alloc+0x73/0x98 [] __kmalloc+0x100/0x12c [] context_struct_to_string+0x106/0x210 [] security_sid_to_context_core+0x10b/0x129 [] security_sid_to_context+0x10/0x12 [] selinux_inode_getsecurity+0x7d/0xa8 [] selinux_inode_getsecctx+0x22/0x2e [] security_inode_getsecctx+0x16/0x18 [] sysfs_setxattr+0x96/0x117 [] __vfs_setxattr_noperm+0x73/0xd9 [] vfs_setxattr+0x83/0xa1 [] setxattr+0xcf/0x101 [] sys_lsetxattr+0x6a/0x8f [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff unreferenced object 0xffff88024163c5a0 (size 96): comm "systemd", pid 1, jiffies 4294683888 (age 94.553s) hex dump (first 32 bytes): 00 00 00 00 ed 41 00 00 00 00 00 00 00 00 00 00 .....A.......... 00 00 00 00 00 00 00 00 0c 64 42 4f 00 00 00 00 .........dBO.... backtrace: [] kmemleak_alloc+0x73/0x98 [] kmem_cache_alloc_trace+0xc4/0xee [] sysfs_init_inode_attrs+0x2a/0x83 [] sysfs_setxattr+0xbf/0x117 [] __vfs_setxattr_noperm+0x73/0xd9 [] vfs_setxattr+0x83/0xa1 [] setxattr+0xcf/0x101 [] sys_lsetxattr+0x6a/0x8f [] system_call_fastpath+0x16/0x1b [] 0xffffffffffffffff ` Signed-off-by: Masami Ichikawa Cc: stable Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 4291fd1..cc7ea5d 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -136,12 +136,13 @@ static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *sec void *old_secdata; size_t old_secdata_len; - iattrs = sd->s_iattr; - if (!iattrs) - iattrs = sysfs_init_inode_attrs(sd); - if (!iattrs) - return -ENOMEM; + if (!sd->s_iattr) { + sd->s_iattr = sysfs_init_inode_attrs(sd); + if (!sd->s_iattr) + return -ENOMEM; + } + iattrs = sd->s_iattr; old_secdata = iattrs->ia_secdata; old_secdata_len = iattrs->ia_secdata_len; -- cgit v1.1 From b6c40b8581d5f2c88d18d7905b9ff6ba9956f506 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 19 Feb 2012 13:10:00 +1100 Subject: w1_bq27000: remove unnecessary NULL test. As recent change means that we now dereference 'dev' before testing for NULL. That means either the change was wrong, or the test isn't needed. As this function is only called from one driver (bq27x000_battery) and it always passed a non-NULL dev, it seems good to assume that the test isn't needed. So remove it. Reported-by: Dan Carpenter Signed-off-by: NeilBrown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 8f10fd2..50d9af7 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -45,9 +45,6 @@ static int w1_bq27000_read(struct device *dev, unsigned int reg) u8 val; struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); - if (!dev) - return 0; - w1_write_8(sl->master, HDQ_CMD_READ | reg); val = w1_read_8(sl->master); -- cgit v1.1 From df7019f37053bb5357907ec2d1fd516304f38348 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 19 Feb 2012 13:10:00 +1100 Subject: w1_bq27000 - remove w1_bq27000_write The function is never used so remove it to avoid bit-rot. It can trivially be re-added if there is ever a need. Signed-off-by: NeilBrown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 50d9af7..6ae60aa 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -26,20 +26,6 @@ static int F_ID; -void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) -{ - struct w1_slave *sl = container_of(dev, struct w1_slave, dev); - - if (!dev) { - pr_info("Could not obtain slave dev ptr\n"); - return; - } - - w1_write_8(sl->master, HDQ_CMD_WRITE | reg); - w1_write_8(sl->master, buf); -} -EXPORT_SYMBOL(w1_bq27000_write); - static int w1_bq27000_read(struct device *dev, unsigned int reg) { u8 val; -- cgit v1.1 From f6e8a1d7b4c4087e13913da43deb45b3e3413f29 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 19 Feb 2012 13:10:00 +1100 Subject: w1_bq27000: Only one thread can access the bq27000 at a time. If multiple threads try, they trip over each other badly. Signed-off-by: NeilBrown Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_bq27000.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c index 6ae60aa..52ad812 100644 --- a/drivers/w1/slaves/w1_bq27000.c +++ b/drivers/w1/slaves/w1_bq27000.c @@ -31,8 +31,10 @@ static int w1_bq27000_read(struct device *dev, unsigned int reg) u8 val; struct w1_slave *sl = container_of(dev->parent, struct w1_slave, dev); + mutex_lock(&sl->master->mutex); w1_write_8(sl->master, HDQ_CMD_READ | reg); val = w1_read_8(sl->master); + mutex_unlock(&sl->master->mutex); return val; } -- cgit v1.1 From fef37e9a47b9927ce2817fe1a0fa8cf40f6eefb6 Mon Sep 17 00:00:00 2001 From: Renata Sayakhova Date: Wed, 29 Feb 2012 14:58:53 +0100 Subject: DS2781 Maxim Stand-Alone Fuel Gauge battery and w1 slave drivers Signed-off-by: Renata Sayakhova Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/power/Kconfig | 14 + drivers/power/Makefile | 1 + drivers/power/ds2781_battery.c | 874 +++++++++++++++++++++++++++++++++++++++++ drivers/w1/slaves/Kconfig | 13 + drivers/w1/slaves/Makefile | 1 + drivers/w1/slaves/w1_ds2781.c | 201 ++++++++++ drivers/w1/slaves/w1_ds2781.h | 136 +++++++ drivers/w1/w1_family.h | 1 + 8 files changed, 1241 insertions(+) create mode 100644 drivers/power/ds2781_battery.c create mode 100644 drivers/w1/slaves/w1_ds2781.c create mode 100644 drivers/w1/slaves/w1_ds2781.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 3a8daf8..459f664 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -76,6 +76,20 @@ config BATTERY_DS2780 help Say Y here to enable support for batteries with ds2780 chip. +config BATTERY_DS2781 + tristate "2781 battery driver" + depends on HAS_IOMEM + select W1 + select W1_SLAVE_DS2781 + help + If you enable this you will have the DS2781 battery driver support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + config BATTERY_DS2782 tristate "DS2782/DS2786 standalone gas-gauge" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index e429008..c590fa5 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o +obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o diff --git a/drivers/power/ds2781_battery.c b/drivers/power/ds2781_battery.c new file mode 100644 index 0000000..ca0d653 --- /dev/null +++ b/drivers/power/ds2781_battery.c @@ -0,0 +1,874 @@ +/* + * 1-wire client/driver for the Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC + * + * Author: Renata Sayakhova + * + * Based on ds2780_battery drivers + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../w1/w1.h" +#include "../w1/slaves/w1_ds2781.h" + +/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ +#define DS2781_CURRENT_UNITS 1563 +/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */ +#define DS2781_CHARGE_UNITS 6250 +/* Number of bytes in user EEPROM space */ +#define DS2781_USER_EEPROM_SIZE (DS2781_EEPROM_BLOCK0_END - \ + DS2781_EEPROM_BLOCK0_START + 1) +/* Number of bytes in parameter EEPROM space */ +#define DS2781_PARAM_EEPROM_SIZE (DS2781_EEPROM_BLOCK1_END - \ + DS2781_EEPROM_BLOCK1_START + 1) + +struct ds2781_device_info { + struct device *dev; + struct power_supply bat; + struct device *w1_dev; + struct task_struct *mutex_holder; +}; + +enum current_types { + CURRENT_NOW, + CURRENT_AVG, +}; + +static const char model[] = "DS2781"; +static const char manufacturer[] = "Maxim/Dallas"; + +static inline struct ds2781_device_info * +to_ds2781_device_info(struct power_supply *psy) +{ + return container_of(psy, struct ds2781_device_info, bat); +} + +static inline struct power_supply *to_power_supply(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static inline int ds2781_battery_io(struct ds2781_device_info *dev_info, + char *buf, int addr, size_t count, int io) +{ + if (dev_info->mutex_holder == current) + return w1_ds2781_io_nolock(dev_info->w1_dev, buf, addr, + count, io); + else + return w1_ds2781_io(dev_info->w1_dev, buf, addr, count, io); +} + +int w1_ds2781_read(struct ds2781_device_info *dev_info, char *buf, + int addr, size_t count) +{ + return ds2781_battery_io(dev_info, buf, addr, count, 0); +} + +static inline int ds2781_read8(struct ds2781_device_info *dev_info, u8 *val, + int addr) +{ + return ds2781_battery_io(dev_info, val, addr, sizeof(u8), 0); +} + +static int ds2781_read16(struct ds2781_device_info *dev_info, s16 *val, + int addr) +{ + int ret; + u8 raw[2]; + + ret = ds2781_battery_io(dev_info, raw, addr, sizeof(raw), 0); + if (ret < 0) + return ret; + + *val = (raw[0] << 8) | raw[1]; + + return 0; +} + +static inline int ds2781_read_block(struct ds2781_device_info *dev_info, + u8 *val, int addr, size_t count) +{ + return ds2781_battery_io(dev_info, val, addr, count, 0); +} + +static inline int ds2781_write(struct ds2781_device_info *dev_info, u8 *val, + int addr, size_t count) +{ + return ds2781_battery_io(dev_info, val, addr, count, 1); +} + +static inline int ds2781_store_eeprom(struct device *dev, int addr) +{ + return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_COPY_DATA); +} + +static inline int ds2781_recall_eeprom(struct device *dev, int addr) +{ + return w1_ds2781_eeprom_cmd(dev, addr, W1_DS2781_RECALL_DATA); +} + +static int ds2781_save_eeprom(struct ds2781_device_info *dev_info, int reg) +{ + int ret; + + ret = ds2781_store_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + ret = ds2781_recall_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + return 0; +} + +/* Set sense resistor value in mhos */ +static int ds2781_set_sense_register(struct ds2781_device_info *dev_info, + u8 conductance) +{ + int ret; + + ret = ds2781_write(dev_info, &conductance, + DS2781_RSNSP, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2781_save_eeprom(dev_info, DS2781_RSNSP); +} + +/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2781_get_rsgain_register(struct ds2781_device_info *dev_info, + u16 *rsgain) +{ + return ds2781_read16(dev_info, rsgain, DS2781_RSGAIN_MSB); +} + +/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2781_set_rsgain_register(struct ds2781_device_info *dev_info, + u16 rsgain) +{ + int ret; + u8 raw[] = {rsgain >> 8, rsgain & 0xFF}; + + ret = ds2781_write(dev_info, raw, + DS2781_RSGAIN_MSB, sizeof(raw)); + if (ret < 0) + return ret; + + return ds2781_save_eeprom(dev_info, DS2781_RSGAIN_MSB); +} + +static int ds2781_get_voltage(struct ds2781_device_info *dev_info, + int *voltage_uV) +{ + int ret; + char val[2]; + int voltage_raw; + + ret = w1_ds2781_read(dev_info, val, DS2781_VOLT_MSB, 2 * sizeof(u8)); + if (ret < 0) + return ret; + /* + * The voltage value is located in 10 bits across the voltage MSB + * and LSB registers in two's compliment form + * Sign bit of the voltage value is in bit 7 of the voltage MSB register + * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the + * voltage MSB register + * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the + * voltage LSB register + */ + voltage_raw = (val[0] << 3) | + (val[1] >> 5); + + /* DS2781 reports voltage in units of 9.76mV, but the battery class + * reports in units of uV, so convert by multiplying by 9760. */ + *voltage_uV = voltage_raw * 9760; + + return 0; +} + +static int ds2781_get_temperature(struct ds2781_device_info *dev_info, + int *temp) +{ + int ret; + char val[2]; + int temp_raw; + + ret = w1_ds2781_read(dev_info, val, DS2781_TEMP_MSB, 2 * sizeof(u8)); + if (ret < 0) + return ret; + /* + * The temperature value is located in 10 bits across the temperature + * MSB and LSB registers in two's compliment form + * Sign bit of the temperature value is in bit 7 of the temperature + * MSB register + * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the + * temperature MSB register + * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the + * temperature LSB register + */ + temp_raw = ((val[0]) << 3) | + (val[1] >> 5); + *temp = temp_raw + (temp_raw / 4); + + return 0; +} + +static int ds2781_get_current(struct ds2781_device_info *dev_info, + enum current_types type, int *current_uA) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw, reg_msb; + + /* + * The units of measurement for current are dependent on the value of + * the sense resistor. + */ + ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -EINVAL; + } + sense_res = 1000 / sense_res_raw; + + if (type == CURRENT_NOW) + reg_msb = DS2781_CURRENT_MSB; + else if (type == CURRENT_AVG) + reg_msb = DS2781_IAVG_MSB; + else + return -EINVAL; + + /* + * The current value is located in 16 bits across the current MSB + * and LSB registers in two's compliment form + * Sign bit of the current value is in bit 7 of the current MSB register + * Bits 14 - 8 of the current value are in bits 6 - 0 of the current + * MSB register + * Bits 7 - 0 of the current value are in bits 7 - 0 of the current + * LSB register + */ + ret = ds2781_read16(dev_info, ¤t_raw, reg_msb); + if (ret < 0) + return ret; + + *current_uA = current_raw * (DS2781_CURRENT_UNITS / sense_res); + return 0; +} + +static int ds2781_get_accumulated_current(struct ds2781_device_info *dev_info, + int *accumulated_current) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw; + + /* + * The units of measurement for accumulated current are dependent on + * the value of the sense resistor. + */ + ret = ds2781_read8(dev_info, &sense_res_raw, DS2781_RSNSP); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -EINVAL; + } + sense_res = 1000 / sense_res_raw; + + /* + * The ACR value is located in 16 bits across the ACR MSB and + * LSB registers + * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR + * MSB register + * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR + * LSB register + */ + ret = ds2781_read16(dev_info, ¤t_raw, DS2781_ACR_MSB); + if (ret < 0) + return ret; + + *accumulated_current = current_raw * (DS2781_CHARGE_UNITS / sense_res); + return 0; +} + +static int ds2781_get_capacity(struct ds2781_device_info *dev_info, + int *capacity) +{ + int ret; + u8 raw; + + ret = ds2781_read8(dev_info, &raw, DS2781_RARC); + if (ret < 0) + return ret; + + *capacity = raw; + return 0; +} + +static int ds2781_get_status(struct ds2781_device_info *dev_info, int *status) +{ + int ret, current_uA, capacity; + + ret = ds2781_get_current(dev_info, CURRENT_NOW, ¤t_uA); + if (ret < 0) + return ret; + + ret = ds2781_get_capacity(dev_info, &capacity); + if (ret < 0) + return ret; + + if (power_supply_am_i_supplied(&dev_info->bat)) { + if (capacity == 100) + *status = POWER_SUPPLY_STATUS_FULL; + else if (current_uA > 50000) + *status = POWER_SUPPLY_STATUS_CHARGING; + else + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + *status = POWER_SUPPLY_STATUS_DISCHARGING; + } + return 0; +} + +static int ds2781_get_charge_now(struct ds2781_device_info *dev_info, + int *charge_now) +{ + int ret; + u16 charge_raw; + + /* + * The RAAC value is located in 16 bits across the RAAC MSB and + * LSB registers + * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC + * MSB register + * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC + * LSB register + */ + ret = ds2781_read16(dev_info, &charge_raw, DS2781_RAAC_MSB); + if (ret < 0) + return ret; + + *charge_now = charge_raw * 1600; + return 0; +} + +static int ds2781_get_control_register(struct ds2781_device_info *dev_info, + u8 *control_reg) +{ + return ds2781_read8(dev_info, control_reg, DS2781_CONTROL); +} + +static int ds2781_set_control_register(struct ds2781_device_info *dev_info, + u8 control_reg) +{ + int ret; + + ret = ds2781_write(dev_info, &control_reg, + DS2781_CONTROL, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2781_save_eeprom(dev_info, DS2781_CONTROL); +} + +static int ds2781_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = ds2781_get_voltage(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_TEMP: + ret = ds2781_get_temperature(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = model; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = manufacturer; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = ds2781_get_current(dev_info, CURRENT_NOW, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = ds2781_get_current(dev_info, CURRENT_AVG, &val->intval); + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = ds2781_get_status(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + ret = ds2781_get_capacity(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = ds2781_get_accumulated_current(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = ds2781_get_charge_now(dev_info, &val->intval); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property ds2781_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_NOW, +}; + +static ssize_t ds2781_get_pmod_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 control_reg; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + /* Get power mode */ + ret = ds2781_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + !!(control_reg & DS2781_CONTROL_PMOD)); +} + +static ssize_t ds2781_set_pmod_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 control_reg, new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + /* Set power mode */ + ret = ds2781_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n"); + return -EINVAL; + } + + if (new_setting) + control_reg |= DS2781_CONTROL_PMOD; + else + control_reg &= ~DS2781_CONTROL_PMOD; + + ret = ds2781_set_control_register(dev_info, control_reg); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2781_get_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sense_resistor; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = ds2781_read8(dev_info, &sense_resistor, DS2781_RSNSP); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sense_resistor); + return ret; +} + +static ssize_t ds2781_set_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + ret = ds2781_set_sense_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2781_get_rsgain_setting(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 rsgain; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = ds2781_get_rsgain_register(dev_info, &rsgain); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", rsgain); +} + +static ssize_t ds2781_set_rsgain_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u16 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = kstrtou16(buf, 0, &new_setting); + if (ret < 0) + return ret; + + /* Gain can only be from 0 to 1.999 in steps of .001 */ + if (new_setting > 1999) { + dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n"); + return -EINVAL; + } + + ret = ds2781_set_rsgain_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2781_get_pio_pin(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sfr; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = ds2781_read8(dev_info, &sfr, DS2781_SFR); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sfr & DS2781_SFR_PIOSC); + return ret; +} + +static ssize_t ds2781_set_pio_pin(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n"); + return -EINVAL; + } + + ret = ds2781_write(dev_info, &new_setting, + DS2781_SFR, sizeof(u8)); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2781_read_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + count = min_t(loff_t, count, + DS2781_EEPROM_BLOCK1_END - + DS2781_EEPROM_BLOCK1_START + 1 - off); + + return ds2781_read_block(dev_info, buf, + DS2781_EEPROM_BLOCK1_START + off, count); +} + +static ssize_t ds2781_write_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2781_EEPROM_BLOCK1_END - + DS2781_EEPROM_BLOCK1_START + 1 - off); + + ret = ds2781_write(dev_info, buf, + DS2781_EEPROM_BLOCK1_START + off, count); + if (ret < 0) + return ret; + + ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK1_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2781_param_eeprom_bin_attr = { + .attr = { + .name = "param_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2781_EEPROM_BLOCK1_END - DS2781_EEPROM_BLOCK1_START + 1, + .read = ds2781_read_param_eeprom_bin, + .write = ds2781_write_param_eeprom_bin, +}; + +static ssize_t ds2781_read_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + + count = min_t(loff_t, count, + DS2781_EEPROM_BLOCK0_END - + DS2781_EEPROM_BLOCK0_START + 1 - off); + + return ds2781_read_block(dev_info, buf, + DS2781_EEPROM_BLOCK0_START + off, count); + +} + +static ssize_t ds2781_write_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2781_device_info *dev_info = to_ds2781_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2781_EEPROM_BLOCK0_END - + DS2781_EEPROM_BLOCK0_START + 1 - off); + + ret = ds2781_write(dev_info, buf, + DS2781_EEPROM_BLOCK0_START + off, count); + if (ret < 0) + return ret; + + ret = ds2781_save_eeprom(dev_info, DS2781_EEPROM_BLOCK0_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2781_user_eeprom_bin_attr = { + .attr = { + .name = "user_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2781_EEPROM_BLOCK0_END - DS2781_EEPROM_BLOCK0_START + 1, + .read = ds2781_read_user_eeprom_bin, + .write = ds2781_write_user_eeprom_bin, +}; + +static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2781_get_pmod_enabled, + ds2781_set_pmod_enabled); +static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR, + ds2781_get_sense_resistor_value, ds2781_set_sense_resistor_value); +static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2781_get_rsgain_setting, + ds2781_set_rsgain_setting); +static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2781_get_pio_pin, + ds2781_set_pio_pin); + + +static struct attribute *ds2781_attributes[] = { + &dev_attr_pmod_enabled.attr, + &dev_attr_sense_resistor_value.attr, + &dev_attr_rsgain_setting.attr, + &dev_attr_pio_pin.attr, + NULL +}; + +static const struct attribute_group ds2781_attr_group = { + .attrs = ds2781_attributes, +}; + +static int __devinit ds2781_battery_probe(struct platform_device *pdev) +{ + int ret = 0; + struct ds2781_device_info *dev_info; + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + ret = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, dev_info); + + dev_info->dev = &pdev->dev; + dev_info->w1_dev = pdev->dev.parent; + dev_info->bat.name = dev_name(&pdev->dev); + dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; + dev_info->bat.properties = ds2781_battery_props; + dev_info->bat.num_properties = ARRAY_SIZE(ds2781_battery_props); + dev_info->bat.get_property = ds2781_battery_get_property; + dev_info->mutex_holder = current; + + ret = power_supply_register(&pdev->dev, &dev_info->bat); + if (ret) { + dev_err(dev_info->dev, "failed to register battery\n"); + goto fail_free_info; + } + + ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); + if (ret) { + dev_err(dev_info->dev, "failed to create sysfs group\n"); + goto fail_unregister; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2781_param_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create param eeprom bin file"); + goto fail_remove_group; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2781_user_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create user eeprom bin file"); + goto fail_remove_bin_file; + } + + dev_info->mutex_holder = NULL; + + return 0; + +fail_remove_bin_file: + sysfs_remove_bin_file(&dev_info->bat.dev->kobj, + &ds2781_param_eeprom_bin_attr); +fail_remove_group: + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); +fail_unregister: + power_supply_unregister(&dev_info->bat); +fail_free_info: + kfree(dev_info); +fail: + return ret; +} + +static int __devexit ds2781_battery_remove(struct platform_device *pdev) +{ + struct ds2781_device_info *dev_info = platform_get_drvdata(pdev); + + dev_info->mutex_holder = current; + + /* remove attributes */ + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2781_attr_group); + + power_supply_unregister(&dev_info->bat); + + kfree(dev_info); + return 0; +} + +static struct platform_driver ds2781_battery_driver = { + .driver = { + .name = "ds2781-battery", + }, + .probe = ds2781_battery_probe, + .remove = __devexit_p(ds2781_battery_remove), +}; + +static int __init ds2781_battery_init(void) +{ + return platform_driver_register(&ds2781_battery_driver); +} + +static void __exit ds2781_battery_exit(void) +{ + platform_driver_unregister(&ds2781_battery_driver); +} + +module_init(ds2781_battery_init); +module_exit(ds2781_battery_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Renata Sayakhova "); +MODULE_DESCRIPTION("Maxim/Dallas DS2781 Stand-Alone Fuel Gauage IC driver"); +MODULE_ALIAS("platform:ds2781-battery"); + diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index d0cb01b..eb9e376 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -81,6 +81,19 @@ config W1_SLAVE_DS2780 If you are unsure, say N. +config W1_SLAVE_DS2781 + tristate "Dallas 2781 battery monitor chip" + depends on W1 + help + If you enable this you will have the DS2781 battery monitor + chip support. + + The battery monitor chip is used in many batteries/devices + as the one who is responsible for charging/discharging/monitoring + Li+ batteries. + + If you are unsure, say N. + config W1_SLAVE_BQ27000 tristate "BQ27000 slave support" depends on W1 diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 1f31e9f..c4f1859 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o +obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o diff --git a/drivers/w1/slaves/w1_ds2781.c b/drivers/w1/slaves/w1_ds2781.c new file mode 100644 index 0000000..0d0c7985 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.c @@ -0,0 +1,201 @@ +/* + * 1-Wire implementation for the ds2781 chip + * + * Author: Renata Sayakhova + * + * Based on w1-ds2780 driver + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" +#include "w1_ds2781.h" + +static int w1_ds2781_do_io(struct device *dev, char *buf, int addr, + size_t count, int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (addr > DS2781_DATA_SIZE || addr < 0) + return 0; + + count = min_t(int, count, DS2781_DATA_SIZE - addr); + + if (w1_reset_select_slave(sl) == 0) { + if (io) { + w1_write_8(sl->master, W1_DS2781_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2781_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } + } + + return count; +} + +int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + int ret; + + if (!dev) + return -ENODEV; + + mutex_lock(&sl->master->mutex); + + ret = w1_ds2781_do_io(dev, buf, addr, count, io); + + mutex_unlock(&sl->master->mutex); + + return ret; +} +EXPORT_SYMBOL(w1_ds2781_io); + +int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr, size_t count, + int io) +{ + int ret; + + if (!dev) + return -ENODEV; + + ret = w1_ds2781_do_io(dev, buf, addr, count, io); + + return ret; +} +EXPORT_SYMBOL(w1_ds2781_io_nolock); + +int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return -EINVAL; + + mutex_lock(&sl->master->mutex); + + if (w1_reset_select_slave(sl) == 0) { + w1_write_8(sl->master, cmd); + w1_write_8(sl->master, addr); + } + + mutex_unlock(&sl->master->mutex); + return 0; +} +EXPORT_SYMBOL(w1_ds2781_eeprom_cmd); + +static ssize_t w1_ds2781_read_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + return w1_ds2781_io(dev, buf, off, count, 0); +} + +static struct bin_attribute w1_ds2781_bin_attr = { + .attr = { + .name = "w1_slave", + .mode = S_IRUGO, + }, + .size = DS2781_DATA_SIZE, + .read = w1_ds2781_read_bin, +}; + +static DEFINE_IDA(bat_ida); + +static int w1_ds2781_add_slave(struct w1_slave *sl) +{ + int ret; + int id; + struct platform_device *pdev; + + id = ida_simple_get(&bat_ida, 0, 0, GFP_KERNEL); + if (id < 0) { + ret = id; + goto noid; + } + + pdev = platform_device_alloc("ds2781-battery", id); + if (!pdev) { + ret = -ENOMEM; + goto pdev_alloc_failed; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr); + if (ret) + goto bin_attr_failed; + + dev_set_drvdata(&sl->dev, pdev); + + return 0; + +bin_attr_failed: +pdev_add_failed: + platform_device_unregister(pdev); +pdev_alloc_failed: + ida_simple_remove(&bat_ida, id); +noid: + return ret; +} + +static void w1_ds2781_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + int id = pdev->id; + + platform_device_unregister(pdev); + ida_simple_remove(&bat_ida, id); + sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2781_bin_attr); +} + +static struct w1_family_ops w1_ds2781_fops = { + .add_slave = w1_ds2781_add_slave, + .remove_slave = w1_ds2781_remove_slave, +}; + +static struct w1_family w1_ds2781_family = { + .fid = W1_FAMILY_DS2781, + .fops = &w1_ds2781_fops, +}; + +static int __init w1_ds2781_init(void) +{ + ida_init(&bat_ida); + return w1_register_family(&w1_ds2781_family); +} + +static void __exit w1_ds2781_exit(void) +{ + w1_unregister_family(&w1_ds2781_family); + ida_destroy(&bat_ida); +} + +module_init(w1_ds2781_init); +module_exit(w1_ds2781_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Renata Sayakhova "); +MODULE_DESCRIPTION("1-wire Driver for Maxim/Dallas DS2781 Stand-Alone Fuel Gauge IC"); diff --git a/drivers/w1/slaves/w1_ds2781.h b/drivers/w1/slaves/w1_ds2781.h new file mode 100644 index 0000000..82bc664 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2781.h @@ -0,0 +1,136 @@ +/* + * 1-Wire implementation for the ds2780 chip + * + * Author: Renata Sayakhova + * + * Based on w1-ds2760 driver + * + * 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. + * + */ + +#ifndef _W1_DS2781_H +#define _W1_DS2781_H + +/* Function commands */ +#define W1_DS2781_READ_DATA 0x69 +#define W1_DS2781_WRITE_DATA 0x6C +#define W1_DS2781_COPY_DATA 0x48 +#define W1_DS2781_RECALL_DATA 0xB8 +#define W1_DS2781_LOCK 0x6A + +/* Register map */ +/* Register 0x00 Reserved */ +#define DS2781_STATUS 0x01 +#define DS2781_RAAC_MSB 0x02 +#define DS2781_RAAC_LSB 0x03 +#define DS2781_RSAC_MSB 0x04 +#define DS2781_RSAC_LSB 0x05 +#define DS2781_RARC 0x06 +#define DS2781_RSRC 0x07 +#define DS2781_IAVG_MSB 0x08 +#define DS2781_IAVG_LSB 0x09 +#define DS2781_TEMP_MSB 0x0A +#define DS2781_TEMP_LSB 0x0B +#define DS2781_VOLT_MSB 0x0C +#define DS2781_VOLT_LSB 0x0D +#define DS2781_CURRENT_MSB 0x0E +#define DS2781_CURRENT_LSB 0x0F +#define DS2781_ACR_MSB 0x10 +#define DS2781_ACR_LSB 0x11 +#define DS2781_ACRL_MSB 0x12 +#define DS2781_ACRL_LSB 0x13 +#define DS2781_AS 0x14 +#define DS2781_SFR 0x15 +#define DS2781_FULL_MSB 0x16 +#define DS2781_FULL_LSB 0x17 +#define DS2781_AE_MSB 0x18 +#define DS2781_AE_LSB 0x19 +#define DS2781_SE_MSB 0x1A +#define DS2781_SE_LSB 0x1B +/* Register 0x1C - 0x1E Reserved */ +#define DS2781_EEPROM 0x1F +#define DS2781_EEPROM_BLOCK0_START 0x20 +/* Register 0x20 - 0x2F User EEPROM */ +#define DS2781_EEPROM_BLOCK0_END 0x2F +/* Register 0x30 - 0x5F Reserved */ +#define DS2781_EEPROM_BLOCK1_START 0x60 +#define DS2781_CONTROL 0x60 +#define DS2781_AB 0x61 +#define DS2781_AC_MSB 0x62 +#define DS2781_AC_LSB 0x63 +#define DS2781_VCHG 0x64 +#define DS2781_IMIN 0x65 +#define DS2781_VAE 0x66 +#define DS2781_IAE 0x67 +#define DS2781_AE_40 0x68 +#define DS2781_RSNSP 0x69 +#define DS2781_FULL_40_MSB 0x6A +#define DS2781_FULL_40_LSB 0x6B +#define DS2781_FULL_4_SLOPE 0x6C +#define DS2781_FULL_3_SLOPE 0x6D +#define DS2781_FULL_2_SLOPE 0x6E +#define DS2781_FULL_1_SLOPE 0x6F +#define DS2781_AE_4_SLOPE 0x70 +#define DS2781_AE_3_SLOPE 0x71 +#define DS2781_AE_2_SLOPE 0x72 +#define DS2781_AE_1_SLOPE 0x73 +#define DS2781_SE_4_SLOPE 0x74 +#define DS2781_SE_3_SLOPE 0x75 +#define DS2781_SE_2_SLOPE 0x76 +#define DS2781_SE_1_SLOPE 0x77 +#define DS2781_RSGAIN_MSB 0x78 +#define DS2781_RSGAIN_LSB 0x79 +#define DS2781_RSTC 0x7A +#define DS2781_COB 0x7B +#define DS2781_TBP34 0x7C +#define DS2781_TBP23 0x7D +#define DS2781_TBP12 0x7E +#define DS2781_EEPROM_BLOCK1_END 0x7F +/* Register 0x7D - 0xFF Reserved */ + +#define DS2781_FSGAIN_MSB 0xB0 +#define DS2781_FSGAIN_LSB 0xB1 + +/* Number of valid register addresses */ +#define DS2781_DATA_SIZE 0xB2 + +/* Status register bits */ +#define DS2781_STATUS_CHGTF (1 << 7) +#define DS2781_STATUS_AEF (1 << 6) +#define DS2781_STATUS_SEF (1 << 5) +#define DS2781_STATUS_LEARNF (1 << 4) +/* Bit 3 Reserved */ +#define DS2781_STATUS_UVF (1 << 2) +#define DS2781_STATUS_PORF (1 << 1) +/* Bit 0 Reserved */ + +/* Control register bits */ +/* Bit 7 Reserved */ +#define DS2781_CONTROL_NBEN (1 << 7) +#define DS2781_CONTROL_UVEN (1 << 6) +#define DS2781_CONTROL_PMOD (1 << 5) +#define DS2781_CONTROL_RNAOP (1 << 4) +#define DS1781_CONTROL_UVTH (1 << 3) +/* Bit 0 - 2 Reserved */ + +/* Special feature register bits */ +/* Bit 1 - 7 Reserved */ +#define DS2781_SFR_PIOSC (1 << 0) + +/* EEPROM register bits */ +#define DS2781_EEPROM_EEC (1 << 7) +#define DS2781_EEPROM_LOCK (1 << 6) +/* Bit 2 - 6 Reserved */ +#define DS2781_EEPROM_BL1 (1 << 1) +#define DS2781_EEPROM_BL0 (1 << 0) + +extern int w1_ds2781_io(struct device *dev, char *buf, int addr, size_t count, + int io); +extern int w1_ds2781_io_nolock(struct device *dev, char *buf, int addr, + size_t count, int io); +extern int w1_ds2781_eeprom_cmd(struct device *dev, int addr, int cmd); + +#endif /* !_W1_DS2781_H */ diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 490cda2..874aeb0 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -38,6 +38,7 @@ #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 #define W1_FAMILY_DS2780 0x32 +#define W1_FAMILY_DS2781 0x3D #define W1_THERM_DS28EA00 0x42 #define MAXNAMELEN 32 -- cgit v1.1 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(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index b858dfd..2c13dea 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 74dda4f..d4ff7ad 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 142e3d600..442b764 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 f62e216..22d6938 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 4668583..2d09bfa 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.1 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(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 2c13dea..6ee17bb 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 d4ff7ad..7050a75 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 442b764..9fa888e 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 22d6938..f62e216 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.1 From 8b0372a258e6bd0e9e5ea3f3d5f05a6bf3972fee Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Mar 2012 12:20:37 -0800 Subject: driver core: minor comment formatting cleanups Came in in the deferred probe patch, quick, clean them up before a kernel janitor finds them and sends me 4 individual patches to fix them up... Cc: Grant Likely Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 9fa888e..1b1cbb5 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -80,8 +80,10 @@ static void deferred_probe_work_func(struct work_struct *work) get_device(dev); - /* Drop the mutex while probing each device; the probe path - * may manipulate the deferred list */ + /* + * 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); @@ -126,16 +128,20 @@ 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 + /* + * 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 */ + * 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. */ + /* + * 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); } @@ -171,8 +177,10 @@ 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 */ + /* + * 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(); -- cgit v1.1 From 7b60a18da393ed70db043a777fd9e6d5363077c4 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Wed, 7 Mar 2012 14:49:56 +0400 Subject: uevent: send events in correct order according to seqnum (v3) The queue handling in the udev daemon assumes that the events are ordered. Before this patch uevent_seqnum is incremented under sequence_lock, than an event is send uner uevent_sock_mutex. I want to say that code contained a window between incrementing seqnum and sending an event. This patch locks uevent_sock_mutex before incrementing uevent_seqnum. v2: delete sequence_lock, uevent_seqnum is protected by uevent_sock_mutex v3: unlock the mutex before the goto exit Thanks for Kay for the comments. Signed-off-by: Andrew Vagin Tested-By: Kay Sievers Cc: stable Signed-off-by: Greg Kroah-Hartman --- lib/kobject_uevent.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index e66e9b6..75cbdb5 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -29,16 +29,17 @@ u64 uevent_seqnum; char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; -static DEFINE_SPINLOCK(sequence_lock); #ifdef CONFIG_NET struct uevent_sock { struct list_head list; struct sock *sk; }; static LIST_HEAD(uevent_sock_list); -static DEFINE_MUTEX(uevent_sock_mutex); #endif +/* This lock protects uevent_seqnum and uevent_sock_list */ +static DEFINE_MUTEX(uevent_sock_mutex); + /* the strings here must match the enum in include/linux/kobject.h */ static const char *kobject_actions[] = { [KOBJ_ADD] = "add", @@ -136,7 +137,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, struct kobject *top_kobj; struct kset *kset; const struct kset_uevent_ops *uevent_ops; - u64 seq; int i = 0; int retval = 0; #ifdef CONFIG_NET @@ -243,17 +243,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, else if (action == KOBJ_REMOVE) kobj->state_remove_uevent_sent = 1; + mutex_lock(&uevent_sock_mutex); /* we will send an event, so request a new sequence number */ - spin_lock(&sequence_lock); - seq = ++uevent_seqnum; - spin_unlock(&sequence_lock); - retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq); - if (retval) + retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum); + if (retval) { + mutex_unlock(&uevent_sock_mutex); goto exit; + } #if defined(CONFIG_NET) /* send netlink message */ - mutex_lock(&uevent_sock_mutex); list_for_each_entry(ue_sk, &uevent_sock_list, list) { struct sock *uevent_sock = ue_sk->sk; struct sk_buff *skb; @@ -290,8 +289,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, } else retval = -ENOMEM; } - mutex_unlock(&uevent_sock_mutex); #endif + mutex_unlock(&uevent_sock_mutex); /* call uevent_helper, usually only enabled during early boot */ if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { -- cgit v1.1 From 54d20f006ceff1f2f1e69d0e54049b6c0765c039 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Mar 2012 13:03:10 -0800 Subject: Revert "sysfs: Kill nlink counting." This reverts commit 524b6c5b39b931311dfe5a2f5abae2f5c9731676. It has shown to break userspace tools, which is not acceptable. Reported-by: Jiri Slaby Cc: Eric W. Biederman Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 6 ++++++ fs/sysfs/inode.c | 3 +++ fs/sysfs/sysfs.h | 1 + 3 files changed, 10 insertions(+) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index dd3779c..2a7a3f5 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -91,6 +91,9 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd) struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; struct rb_node *parent = NULL; + if (sysfs_type(sd) == SYSFS_DIR) + sd->s_parent->s_dir.subdirs++; + while (*node) { struct sysfs_dirent *pos; int result; @@ -123,6 +126,9 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd) */ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) { + if (sysfs_type(sd) == SYSFS_DIR) + sd->s_parent->s_dir.subdirs--; + rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index cc7ea5d..feb2d69 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -217,6 +217,9 @@ static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) iattrs->ia_secdata, iattrs->ia_secdata_len); } + + if (sysfs_type(sd) == SYSFS_DIR) + set_nlink(inode, sd->s_dir.subdirs + 2); } int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6289a00..661a963 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -19,6 +19,7 @@ struct sysfs_open_dirent; struct sysfs_elem_dir { struct kobject *kobj; + unsigned long subdirs; /* children rbtree starts here and goes through sd->s_rb */ struct rb_root children; }; -- cgit v1.1 From 04bf30115f4ba2beda1efb6cfbae49cdc757f3a9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Mar 2012 13:07:56 +0000 Subject: regulator: Support driver probe deferral If we fail to locate a requested regulator return -EPROBE_DEFER. If drivers pass this error code through to their caller (which they really should) then this will ensure that the probe is retried later when further devices become available. In the unusual case where a driver doesn't want this it can override the default behaviour. Signed-off-by: Mark Brown Acked-by: Grant Likely Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e9a83f8..fcde037 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1210,7 +1210,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, { struct regulator_dev *rdev; struct regulator_map *map; - struct regulator *regulator = ERR_PTR(-ENODEV); + struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); const char *devname = NULL; int ret; @@ -2834,7 +2834,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, if (!r) { dev_err(dev, "Failed to find supply %s\n", supply); - ret = -ENODEV; + ret = -EPROBE_DEFER; goto scrub; } -- cgit v1.1 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(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 0ef4c1f..779109b 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 e57a6c6..a2d8c54 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 4ebf703..00d3f7c 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.1 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(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 779109b..cfe60b0 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 a2d8c54..e88a979 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 00d3f7c..a98878c 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.1 From db425334e5bb7fa65bbbd7bea9d79842f65bcf45 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 16 Mar 2012 08:02:26 -0700 Subject: Tools: hv: Fully support the new KVP verbs in the user level daemon Now fully support the new KVP messages in the user level daemon. Hyper-V defines multiple persistent pools to which the host can write/read/modify KVP tuples. In this patch we implement a file for each specified pool, where the KVP tuples will be stored in the guest. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Reviewed-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- tools/hv/hv_kvp_daemon.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 1 deletion(-) diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index a98878c..2fb9c3d 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -39,7 +39,8 @@ #include #include #include - +#include +#include /* * KVP protocol: The user mode component first registers with the @@ -79,6 +80,250 @@ static char *os_build; static char *lic_version; static struct utsname uts_buf; + +#define MAX_FILE_NAME 100 +#define ENTRIES_PER_BLOCK 50 + +struct kvp_record { + __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; + __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; +}; + +struct kvp_file_state { + int fd; + int num_blocks; + struct kvp_record *records; + int num_records; + __u8 fname[MAX_FILE_NAME]; +}; + +static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; + +static void kvp_acquire_lock(int pool) +{ + struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; + fl.l_pid = getpid(); + + if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { + syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); + exit(-1); + } +} + +static void kvp_release_lock(int pool) +{ + struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; + fl.l_pid = getpid(); + + if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { + perror("fcntl"); + syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); + exit(-1); + } +} + +static void kvp_update_file(int pool) +{ + FILE *filep; + size_t bytes_written; + + /* + * We are going to write our in-memory registry out to + * disk; acquire the lock first. + */ + kvp_acquire_lock(pool); + + filep = fopen(kvp_file_info[pool].fname, "w"); + if (!filep) { + kvp_release_lock(pool); + syslog(LOG_ERR, "Failed to open file, pool: %d", pool); + exit(-1); + } + + bytes_written = fwrite(kvp_file_info[pool].records, + sizeof(struct kvp_record), + kvp_file_info[pool].num_records, filep); + + fflush(filep); + kvp_release_lock(pool); +} + +static int kvp_file_init(void) +{ + int ret, fd; + FILE *filep; + size_t records_read; + __u8 *fname; + struct kvp_record *record; + struct kvp_record *readp; + int num_blocks; + int i; + int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; + + if (access("/var/opt/hyperv", F_OK)) { + if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { + syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); + exit(-1); + } + } + + for (i = 0; i < KVP_POOL_COUNT; i++) { + fname = kvp_file_info[i].fname; + records_read = 0; + num_blocks = 1; + sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); + fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); + + if (fd == -1) + return 1; + + + filep = fopen(fname, "r"); + if (!filep) + return 1; + + record = malloc(alloc_unit * num_blocks); + if (record == NULL) { + fclose(filep); + return 1; + } + while (!feof(filep)) { + readp = &record[records_read]; + records_read += fread(readp, sizeof(struct kvp_record), + ENTRIES_PER_BLOCK, + filep); + + if (!feof(filep)) { + /* + * We have more data to read. + */ + num_blocks++; + record = realloc(record, alloc_unit * + num_blocks); + if (record == NULL) { + fclose(filep); + return 1; + } + continue; + } + break; + } + kvp_file_info[i].fd = fd; + kvp_file_info[i].num_blocks = num_blocks; + kvp_file_info[i].records = record; + kvp_file_info[i].num_records = records_read; + fclose(filep); + + } + + return 0; +} + +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; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just move the remaining + * entries up. + */ + if (i == num_records) { + kvp_file_info[pool].num_records--; + kvp_update_file(pool); + return 0; + } + + j = i; + k = j + 1; + for (; k < num_records; k++) { + strcpy(record[j].key, record[k].key); + strcpy(record[j].value, record[k].value); + j++; + } + + kvp_file_info[pool].num_records--; + kvp_update_file(pool); + return 0; + } + return 1; +} + +static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, + int value_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_blocks = kvp_file_info[pool].num_blocks; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just update the value - + * this is the modify case. + */ + memcpy(record[i].value, value, value_size); + kvp_update_file(pool); + return 0; + } + + /* + * Need to add a new entry; + */ + if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { + /* Need to allocate a larger array for reg entries. */ + record = realloc(record, sizeof(struct kvp_record) * + ENTRIES_PER_BLOCK * (num_blocks + 1)); + + if (record == NULL) + return 1; + kvp_file_info[pool].num_blocks++; + + } + memcpy(record[i].value, value, value_size); + memcpy(record[i].key, key, key_size); + kvp_file_info[pool].records = record; + kvp_file_info[pool].num_records++; + kvp_update_file(pool); + return 0; +} + +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; + + if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || + (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) + return 1; + + for (i = 0; i < num_records; i++) { + if (memcmp(key, record[i].key, key_size)) + continue; + /* + * Found a match; just copy the value out. + */ + memcpy(value, record[i].value, value_size); + return 0; + } + + return 1; +} + void kvp_get_os_info(void) { FILE *file; @@ -315,6 +560,11 @@ int main(void) */ kvp_get_os_info(); + if (kvp_file_init()) { + syslog(LOG_ERR, "Failed to initialize the pools"); + exit(-1); + } + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (fd < 0) { syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); @@ -389,9 +639,38 @@ int main(void) } continue; + /* + * The current protocol with the kernel component uses a + * NULL key name to pass an error condition. + * For the SET, GET and DELETE operations, + * use the existing protocol to pass back error. + */ + case KVP_OP_SET: + if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, + hv_msg->body.kvp_set.data.key, + hv_msg->body.kvp_set.data.key_size, + hv_msg->body.kvp_set.data.value, + hv_msg->body.kvp_set.data.value_size)) + strcpy(hv_msg->body.kvp_set.data.key, ""); + break; + case KVP_OP_GET: + if (kvp_get_value(hv_msg->kvp_hdr.pool, + hv_msg->body.kvp_set.data.key, + hv_msg->body.kvp_set.data.key_size, + hv_msg->body.kvp_set.data.value, + hv_msg->body.kvp_set.data.value_size)) + strcpy(hv_msg->body.kvp_set.data.key, ""); + break; + case KVP_OP_DELETE: + if (kvp_key_delete(hv_msg->kvp_hdr.pool, + hv_msg->body.kvp_delete.key, + hv_msg->body.kvp_delete.key_size)) + strcpy(hv_msg->body.kvp_delete.key, ""); + break; + default: break; } -- cgit v1.1 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(-) diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index cfe60b0..6186025 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 e88a979..5852545 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 2fb9c3d..146fd61 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.1