Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux...
[pandora-kernel.git] / fs / sysfs / dir.c
index f05f230..7e54bac 100644 (file)
@@ -93,7 +93,7 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
  *     RETURNS:
  *     Pointer to @sd on success, NULL on failure.
  */
-static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
+struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
 {
        if (unlikely(!sd))
                return NULL;
@@ -106,8 +106,10 @@ static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
                        return NULL;
 
                t = atomic_cmpxchg(&sd->s_active, v, v + 1);
-               if (likely(t == v))
+               if (likely(t == v)) {
+                       rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
                        return sd;
+               }
                if (t < 0)
                        return NULL;
 
@@ -122,7 +124,7 @@ static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
  *     Put an active reference to @sd.  This function is noop if @sd
  *     is NULL.
  */
-static void sysfs_put_active(struct sysfs_dirent *sd)
+void sysfs_put_active(struct sysfs_dirent *sd)
 {
        struct completion *cmpl;
        int v;
@@ -130,6 +132,7 @@ static void sysfs_put_active(struct sysfs_dirent *sd)
        if (unlikely(!sd))
                return;
 
+       rwsem_release(&sd->dep_map, 1, _RET_IP_);
        v = atomic_dec_return(&sd->s_active);
        if (likely(v != SD_DEACTIVATED_BIAS))
                return;
@@ -141,45 +144,6 @@ static void sysfs_put_active(struct sysfs_dirent *sd)
        complete(cmpl);
 }
 
-/**
- *     sysfs_get_active_two - get active references to sysfs_dirent and parent
- *     @sd: sysfs_dirent of interest
- *
- *     Get active reference to @sd and its parent.  Parent's active
- *     reference is grabbed first.  This function is noop if @sd is
- *     NULL.
- *
- *     RETURNS:
- *     Pointer to @sd on success, NULL on failure.
- */
-struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd)
-{
-       if (sd) {
-               if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent)))
-                       return NULL;
-               if (unlikely(!sysfs_get_active(sd))) {
-                       sysfs_put_active(sd->s_parent);
-                       return NULL;
-               }
-       }
-       return sd;
-}
-
-/**
- *     sysfs_put_active_two - put active references to sysfs_dirent and parent
- *     @sd: sysfs_dirent of interest
- *
- *     Put active references to @sd and its parent.  This function is
- *     noop if @sd is NULL.
- */
-void sysfs_put_active_two(struct sysfs_dirent *sd)
-{
-       if (sd) {
-               sysfs_put_active(sd);
-               sysfs_put_active(sd->s_parent);
-       }
-}
-
 /**
  *     sysfs_deactivate - deactivate sysfs_dirent
  *     @sd: sysfs_dirent to deactivate
@@ -192,17 +156,27 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
        int v;
 
        BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED));
+
+       if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF))
+               return;
+
        sd->s_sibling = (void *)&wait;
 
+       rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_);
        /* atomic_add_return() is a mb(), put_active() will always see
         * the updated sd->s_sibling.
         */
        v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
 
-       if (v != SD_DEACTIVATED_BIAS)
+       if (v != SD_DEACTIVATED_BIAS) {
+               lock_contended(&sd->dep_map, _RET_IP_);
                wait_for_completion(&wait);
+       }
 
        sd->s_sibling = NULL;
+
+       lock_acquired(&sd->dep_map, _RET_IP_);
+       rwsem_release(&sd->dep_map, 1, _RET_IP_);
 }
 
 static int sysfs_alloc_ino(ino_t *pino)
@@ -406,7 +380,7 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 {
        struct sysfs_inode_attrs *ps_iattr;
 
-       if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
+       if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
                return -EEXIST;
 
        sd->s_parent = sysfs_get(acxt->parent_sd);
@@ -559,13 +533,17 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
  *     Pointer to sysfs_dirent if found, NULL if not.
  */
 struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
+                                      const void *ns,
                                       const unsigned char *name)
 {
        struct sysfs_dirent *sd;
 
-       for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
+       for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) {
+               if (ns && sd->s_ns && (sd->s_ns != ns))
+                       continue;
                if (!strcmp(sd->s_name, name))
                        return sd;
+       }
        return NULL;
 }
 
@@ -584,12 +562,13 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
  *     Pointer to sysfs_dirent if found, NULL if not.
  */
 struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
+                                     const void *ns,
                                      const unsigned char *name)
 {
        struct sysfs_dirent *sd;
 
        mutex_lock(&sysfs_mutex);
-       sd = sysfs_find_dirent(parent_sd, name);
+       sd = sysfs_find_dirent(parent_sd, ns, name);
        sysfs_get(sd);
        mutex_unlock(&sysfs_mutex);
 
@@ -598,7 +577,8 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
 EXPORT_SYMBOL_GPL(sysfs_get_dirent);
 
 static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
-                     const char *name, struct sysfs_dirent **p_sd)
+       enum kobj_ns_type type, const void *ns, const char *name,
+       struct sysfs_dirent **p_sd)
 {
        umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
        struct sysfs_addrm_cxt acxt;
@@ -609,6 +589,9 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
        sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
        if (!sd)
                return -ENOMEM;
+
+       sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
+       sd->s_ns = ns;
        sd->s_dir.kobj = kobj;
 
        /* link in */
@@ -627,7 +610,33 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
 int sysfs_create_subdir(struct kobject *kobj, const char *name,
                        struct sysfs_dirent **p_sd)
 {
-       return create_dir(kobj, kobj->sd, name, p_sd);
+       return create_dir(kobj, kobj->sd,
+                         KOBJ_NS_TYPE_NONE, NULL, name, p_sd);
+}
+
+/**
+ *     sysfs_read_ns_type: return associated ns_type
+ *     @kobj: the kobject being queried
+ *
+ *     Each kobject can be tagged with exactly one namespace type
+ *     (i.e. network or user).  Return the ns_type associated with
+ *     this object if any
+ */
+static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj)
+{
+       const struct kobj_ns_type_operations *ops;
+       enum kobj_ns_type type;
+
+       ops = kobj_child_ns_ops(kobj);
+       if (!ops)
+               return KOBJ_NS_TYPE_NONE;
+
+       type = ops->type;
+       BUG_ON(type <= KOBJ_NS_TYPE_NONE);
+       BUG_ON(type >= KOBJ_NS_TYPES);
+       BUG_ON(!kobj_ns_type_registered(type));
+
+       return type;
 }
 
 /**
@@ -636,7 +645,9 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name,
  */
 int sysfs_create_dir(struct kobject * kobj)
 {
+       enum kobj_ns_type type;
        struct sysfs_dirent *parent_sd, *sd;
+       const void *ns = NULL;
        int error = 0;
 
        BUG_ON(!kobj);
@@ -646,7 +657,11 @@ int sysfs_create_dir(struct kobject * kobj)
        else
                parent_sd = &sysfs_root;
 
-       error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
+       if (sysfs_ns_type(parent_sd))
+               ns = kobj->ktype->namespace(kobj);
+       type = sysfs_read_ns_type(kobj);
+
+       error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
        if (!error)
                kobj->sd = sd;
        return error;
@@ -656,13 +671,19 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
                                struct nameidata *nd)
 {
        struct dentry *ret = NULL;
-       struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
+       struct dentry *parent = dentry->d_parent;
+       struct sysfs_dirent *parent_sd = parent->d_fsdata;
        struct sysfs_dirent *sd;
        struct inode *inode;
+       enum kobj_ns_type type;
+       const void *ns;
 
        mutex_lock(&sysfs_mutex);
 
-       sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dir->i_sb)->ns[type];
+
+       sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
 
        /* no such entry */
        if (!sd) {
@@ -671,7 +692,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        /* attach dentry and inode */
-       inode = sysfs_get_inode(sd);
+       inode = sysfs_get_inode(dir->i_sb, sd);
        if (!inode) {
                ret = ERR_PTR(-ENOMEM);
                goto out_unlock;
@@ -761,7 +782,8 @@ void sysfs_remove_dir(struct kobject * kobj)
 }
 
 int sysfs_rename(struct sysfs_dirent *sd,
-       struct sysfs_dirent *new_parent_sd, const char *new_name)
+       struct sysfs_dirent *new_parent_sd, const void *new_ns,
+       const char *new_name)
 {
        const char *dup_name = NULL;
        int error;
@@ -769,12 +791,12 @@ int sysfs_rename(struct sysfs_dirent *sd,
        mutex_lock(&sysfs_mutex);
 
        error = 0;
-       if ((sd->s_parent == new_parent_sd) &&
+       if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) &&
            (strcmp(sd->s_name, new_name) == 0))
                goto out;       /* nothing to rename */
 
        error = -EEXIST;
-       if (sysfs_find_dirent(new_parent_sd, new_name))
+       if (sysfs_find_dirent(new_parent_sd, new_ns, new_name))
                goto out;
 
        /* rename sysfs_dirent */
@@ -796,6 +818,7 @@ int sysfs_rename(struct sysfs_dirent *sd,
                sd->s_parent = new_parent_sd;
                sysfs_link_sibling(sd);
        }
+       sd->s_ns = new_ns;
 
        error = 0;
  out:
@@ -806,19 +829,28 @@ int sysfs_rename(struct sysfs_dirent *sd,
 
 int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
 {
-       return sysfs_rename(kobj->sd, kobj->sd->s_parent, new_name);
+       struct sysfs_dirent *parent_sd = kobj->sd->s_parent;
+       const void *new_ns = NULL;
+
+       if (sysfs_ns_type(parent_sd))
+               new_ns = kobj->ktype->namespace(kobj);
+
+       return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name);
 }
 
 int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
 {
        struct sysfs_dirent *sd = kobj->sd;
        struct sysfs_dirent *new_parent_sd;
+       const void *new_ns = NULL;
 
        BUG_ON(!sd->s_parent);
+       if (sysfs_ns_type(sd->s_parent))
+               new_ns = kobj->ktype->namespace(kobj);
        new_parent_sd = new_parent_kobj && new_parent_kobj->sd ?
                new_parent_kobj->sd : &sysfs_root;
 
-       return sysfs_rename(sd, new_parent_sd, sd->s_name);
+       return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name);
 }
 
 /* Relationship between s_mode and the DT_xxx types */
@@ -827,13 +859,56 @@ static inline unsigned char dt_type(struct sysfs_dirent *sd)
        return (sd->s_mode >> 12) & 15;
 }
 
+static int sysfs_dir_release(struct inode *inode, struct file *filp)
+{
+       sysfs_put(filp->private_data);
+       return 0;
+}
+
+static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
+       struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
+{
+       if (pos) {
+               int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
+                       pos->s_parent == parent_sd &&
+                       ino == pos->s_ino;
+               sysfs_put(pos);
+               if (!valid)
+                       pos = NULL;
+       }
+       if (!pos && (ino > 1) && (ino < INT_MAX)) {
+               pos = parent_sd->s_dir.children;
+               while (pos && (ino > pos->s_ino))
+                       pos = pos->s_sibling;
+       }
+       while (pos && pos->s_ns && pos->s_ns != ns)
+               pos = pos->s_sibling;
+       return pos;
+}
+
+static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
+       struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
+{
+       pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
+       if (pos)
+               pos = pos->s_sibling;
+       while (pos && pos->s_ns && pos->s_ns != ns)
+               pos = pos->s_sibling;
+       return pos;
+}
+
 static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 {
        struct dentry *dentry = filp->f_path.dentry;
        struct sysfs_dirent * parent_sd = dentry->d_fsdata;
-       struct sysfs_dirent *pos;
+       struct sysfs_dirent *pos = filp->private_data;
+       enum kobj_ns_type type;
+       const void *ns;
        ino_t ino;
 
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dentry->d_sb)->ns[type];
+
        if (filp->f_pos == 0) {
                ino = parent_sd->s_ino;
                if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)
@@ -847,29 +922,31 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
                if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
                        filp->f_pos++;
        }
-       if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) {
-               mutex_lock(&sysfs_mutex);
-
-               /* Skip the dentries we have already reported */
-               pos = parent_sd->s_dir.children;
-               while (pos && (filp->f_pos > pos->s_ino))
-                       pos = pos->s_sibling;
-
-               for ( ; pos; pos = pos->s_sibling) {
-                       const char * name;
-                       int len;
-
-                       name = pos->s_name;
-                       len = strlen(name);
-                       filp->f_pos = ino = pos->s_ino;
+       mutex_lock(&sysfs_mutex);
+       for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos);
+            pos;
+            pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) {
+               const char * name;
+               unsigned int type;
+               int len, ret;
+
+               name = pos->s_name;
+               len = strlen(name);
+               ino = pos->s_ino;
+               type = dt_type(pos);
+               filp->f_pos = ino;
+               filp->private_data = sysfs_get(pos);
 
-                       if (filldir(dirent, name, len, filp->f_pos, ino,
-                                        dt_type(pos)) < 0)
-                               break;
-               }
-               if (!pos)
-                       filp->f_pos = INT_MAX;
                mutex_unlock(&sysfs_mutex);
+               ret = filldir(dirent, name, len, filp->f_pos, ino, type);
+               mutex_lock(&sysfs_mutex);
+               if (ret < 0)
+                       break;
+       }
+       mutex_unlock(&sysfs_mutex);
+       if ((filp->f_pos > 1) && !pos) { /* EOF */
+               filp->f_pos = INT_MAX;
+               filp->private_data = NULL;
        }
        return 0;
 }
@@ -878,5 +955,6 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
 const struct file_operations sysfs_dir_operations = {
        .read           = generic_read_dir,
        .readdir        = sysfs_readdir,
+       .release        = sysfs_dir_release,
        .llseek         = generic_file_llseek,
 };