aboutsummaryrefslogtreecommitdiffstats
path: root/fs/sysfs/dir.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2010-01-01 17:43:53 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-07 20:04:48 -0500
commit1e5289c97bba2d8ee7239a416bc3f28743362cd9 (patch)
tree5a9c422b6dc16e4084866d548c043994da37e3d1 /fs/sysfs/dir.c
parent3f5468c9ae293cbca43e4ffe6ca3b3235189042a (diff)
sysfs: Cache the last sysfs_dirent to improve readdir scalability v2
When sysfs_readdir stops short we now cache the next sysfs_dirent to return to user space in filp->private_data. There is no impact on the rest of sysfs by doing this and in the common case it allows us to pick up exactly where we left off with no seeking. Additionally I drop and regrab the sysfs_mutex around filldir to avoid a page fault abritrarily increasing the hold time on the sysfs_mutex. v2: Returned to using INT_MAX as the EOF condition. seekdir is ambiguous unless all directory entries have a unique f_pos value. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=14949 Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r--fs/sysfs/dir.c82
1 files changed, 60 insertions, 22 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 699f371b9f12..5c4703d751b5 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -837,11 +837,46 @@ static inline unsigned char dt_type(struct sysfs_dirent *sd)
837 return (sd->s_mode >> 12) & 15; 837 return (sd->s_mode >> 12) & 15;
838} 838}
839 839
840static int sysfs_dir_release(struct inode *inode, struct file *filp)
841{
842 sysfs_put(filp->private_data);
843 return 0;
844}
845
846static struct sysfs_dirent *sysfs_dir_pos(struct sysfs_dirent *parent_sd,
847 ino_t ino, struct sysfs_dirent *pos)
848{
849 if (pos) {
850 int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
851 pos->s_parent == parent_sd &&
852 ino == pos->s_ino;
853 sysfs_put(pos);
854 if (valid)
855 return pos;
856 }
857 pos = NULL;
858 if ((ino > 1) && (ino < INT_MAX)) {
859 pos = parent_sd->s_dir.children;
860 while (pos && (ino > pos->s_ino))
861 pos = pos->s_sibling;
862 }
863 return pos;
864}
865
866static struct sysfs_dirent *sysfs_dir_next_pos(struct sysfs_dirent *parent_sd,
867 ino_t ino, struct sysfs_dirent *pos)
868{
869 pos = sysfs_dir_pos(parent_sd, ino, pos);
870 if (pos)
871 pos = pos->s_sibling;
872 return pos;
873}
874
840static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) 875static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
841{ 876{
842 struct dentry *dentry = filp->f_path.dentry; 877 struct dentry *dentry = filp->f_path.dentry;
843 struct sysfs_dirent * parent_sd = dentry->d_fsdata; 878 struct sysfs_dirent * parent_sd = dentry->d_fsdata;
844 struct sysfs_dirent *pos; 879 struct sysfs_dirent *pos = filp->private_data;
845 ino_t ino; 880 ino_t ino;
846 881
847 if (filp->f_pos == 0) { 882 if (filp->f_pos == 0) {
@@ -857,29 +892,31 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
857 if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0) 892 if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
858 filp->f_pos++; 893 filp->f_pos++;
859 } 894 }
860 if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) { 895 mutex_lock(&sysfs_mutex);
861 mutex_lock(&sysfs_mutex); 896 for (pos = sysfs_dir_pos(parent_sd, filp->f_pos, pos);
862 897 pos;
863 /* Skip the dentries we have already reported */ 898 pos = sysfs_dir_next_pos(parent_sd, filp->f_pos, pos)) {
864 pos = parent_sd->s_dir.children; 899 const char * name;
865 while (pos && (filp->f_pos > pos->s_ino)) 900 unsigned int type;
866 pos = pos->s_sibling; 901 int len, ret;
867 902
868 for ( ; pos; pos = pos->s_sibling) { 903 name = pos->s_name;
869 const char * name; 904 len = strlen(name);
870 int len; 905 ino = pos->s_ino;
871 906 type = dt_type(pos);
872 name = pos->s_name; 907 filp->f_pos = ino;
873 len = strlen(name); 908 filp->private_data = sysfs_get(pos);
874 filp->f_pos = ino = pos->s_ino;
875 909
876 if (filldir(dirent, name, len, filp->f_pos, ino,
877 dt_type(pos)) < 0)
878 break;
879 }
880 if (!pos)
881 filp->f_pos = INT_MAX;
882 mutex_unlock(&sysfs_mutex); 910 mutex_unlock(&sysfs_mutex);
911 ret = filldir(dirent, name, len, filp->f_pos, ino, type);
912 mutex_lock(&sysfs_mutex);
913 if (ret < 0)
914 break;
915 }
916 mutex_unlock(&sysfs_mutex);
917 if ((filp->f_pos > 1) && !pos) { /* EOF */
918 filp->f_pos = INT_MAX;
919 filp->private_data = NULL;
883 } 920 }
884 return 0; 921 return 0;
885} 922}
@@ -888,5 +925,6 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
888const struct file_operations sysfs_dir_operations = { 925const struct file_operations sysfs_dir_operations = {
889 .read = generic_read_dir, 926 .read = generic_read_dir,
890 .readdir = sysfs_readdir, 927 .readdir = sysfs_readdir,
928 .release = sysfs_dir_release,
891 .llseek = generic_file_llseek, 929 .llseek = generic_file_llseek,
892}; 930};