[PATCH] sysfs: Optionally count subdirectories to support buggy applications

From: Eric W. Biederman
Date: Wed Feb 01 2012 - 17:19:22 EST



lm_sensors and possibly other applications get confused if all sysfs
directories return nlink == 1. The lm_sensors code that got confused
was just wrong and a fixed version of lm_sensors should be released
shortly.

There may be other applications that have problems with sysfs return
nlink == 1 for directories. To allow people to continue to use old
versions of userspace with new kernels add to sysfs a compile time
option to maintain mostly precise directory counts for those people who
don't mind the cost.

I have moved where we keep nlink in sysfs_dirent as compared to previous
versions of subdirectory counting to a location that packs better.

Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxx>
---
fs/sysfs/Kconfig | 15 +++++++++++++++
fs/sysfs/dir.c | 8 ++++++++
fs/sysfs/inode.c | 2 ++
fs/sysfs/sysfs.h | 38 ++++++++++++++++++++++++++++++++++++++
4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/fs/sysfs/Kconfig b/fs/sysfs/Kconfig
index 8c41fea..9b403e9 100644
--- a/fs/sysfs/Kconfig
+++ b/fs/sysfs/Kconfig
@@ -21,3 +21,18 @@ config SYSFS
example, "root=03:01" for /dev/hda1.

Designers of embedded systems may wish to say N here to conserve space.
+
+config SYSFS_COUNT_LINKS
+ bool "sysfs count subdirectoires to support buggy applications"
+ default n
+ help
+
+ Increase the memory and cpu untilization of sysfs by maintaining
+ by maintaining a count of how hard links from subdirectories a
+ directory has. This allows sysfs to report the directory nlink
+ as the number of subdirectories plus two. With out this enabled
+ sysfs will report the nlink of all directories as 1, which is
+ the standard way of indicating that the number of subdirectoires
+ is not tracked.
+
+ Unless you know you need this say N here.
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index dd3779c..f30df7c 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)
+ sysfs_inc_nlink(sd->s_parent);
+
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)
+ sysfs_dec_nlink(sd->s_parent);
+
rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
}

@@ -367,6 +373,8 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
sd->s_mode = mode;
sd->s_flags = type;

+ sysfs_init_nlink(sd);
+
return sd;

err_out2:
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 4291fd1..782c66a 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -216,6 +216,8 @@ static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
iattrs->ia_secdata,
iattrs->ia_secdata_len);
}
+
+ set_nlink(inode, sysfs_read_nlink(sd));
}

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..4603506 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -69,6 +69,9 @@ struct sysfs_dirent {

const void *s_ns; /* namespace tag */
unsigned int s_hash; /* ns + name hash */
+#ifdef CONFIG_SYSFS_COUNT_LINKS
+ unsigned int s_nlink;
+#endif
union {
struct sysfs_elem_dir s_dir;
struct sysfs_elem_symlink s_symlink;
@@ -127,6 +130,41 @@ do { \
#define sysfs_dirent_init_lockdep(sd) do {} while(0)
#endif

+#ifdef CONFIG_SYSFS_COUNT_LINKS
+static inline void sysfs_init_nlink(struct sysfs_dirent *sd)
+{
+ if (sysfs_type(sd) == SYSFS_DIR)
+ sd->s_nlink = 2;
+ else
+ sd->s_nlink = 1;
+}
+static inline void sysfs_inc_nlink(struct sysfs_dirent *sd)
+{
+ sd->s_nlink++;
+}
+static inline void sysfs_dec_nlink(struct sysfs_dirent *sd)
+{
+ sd->s_nlink++;
+}
+static inline unsigned int sysfs_read_nlink(struct sysfs_dirent *sd)
+{
+ return sd->s_nlink;
+}
+#else
+static inline void sysfs_init_nlink(struct sysfs_dirent *sd)
+{
+}
+static inline void sysfs_inc_nlink(struct sysfs_dirent *sd)
+{
+}
+static inline void sysfs_dec_nlink(struct sysfs_dirent *sd)
+{
+}
+static inline unsigned int sysfs_read_nlink(struct sysfs_dirent *sd)
+{
+ return 1;
+}
+#endif
/*
* Context structure to be used while adding/removing nodes.
*/
--
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/