aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2007-08-20 08:36:30 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-10-12 17:51:08 -0400
commit3efa65b92d832873ece62b42a4268c2515943977 (patch)
treec7d09674edfd683e614649564e9159066dfc1d40
parent94777e09180b6249d455baa2dbe34cf630e0c033 (diff)
sysfs: Simplify readdir.
At some point someone wrote sysfs_readdir to insert a cursor into the list of sysfs_dirents to ensure that sysfs_readdir would restart properly. That works but it is complex code and tends to be expensive. The same effect can be achieved by keeping the sysfs_dirents in inode order and using the inode number as the f_pos. Then when we restart we just have to find the first dirent whose inode number is equal or greater then the last sysfs_dirent we attempted to return. Removing the sysfs directory cursor also allows the remove of all of the mysterious checks for sysfs_type(sd) != 0. Which were nonbovious checks to see if a cursor was in a directory list. tj: offset marker for EOF is changed from UINT_MAX to INT_MAX to avoid overflow in case offset is 32bit. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Tejun Heo <htejun@gmail.com> Cc: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--fs/sysfs/dir.c175
1 files changed, 44 insertions, 131 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index b72b42ed80d1..953e8432b0ae 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -33,10 +33,20 @@ static DEFINE_IDA(sysfs_ino_ida);
33static void sysfs_link_sibling(struct sysfs_dirent *sd) 33static void sysfs_link_sibling(struct sysfs_dirent *sd)
34{ 34{
35 struct sysfs_dirent *parent_sd = sd->s_parent; 35 struct sysfs_dirent *parent_sd = sd->s_parent;
36 struct sysfs_dirent **pos;
36 37
37 BUG_ON(sd->s_sibling); 38 BUG_ON(sd->s_sibling);
38 sd->s_sibling = parent_sd->s_children; 39
39 parent_sd->s_children = sd; 40 /* Store directory entries in order by ino. This allows
41 * readdir to properly restart without having to add a
42 * cursor into the s_children list.
43 */
44 for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) {
45 if (sd->s_ino < (*pos)->s_ino)
46 break;
47 }
48 sd->s_sibling = *pos;
49 *pos = sd;
40} 50}
41 51
42/** 52/**
@@ -659,7 +669,7 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
659 struct sysfs_dirent *sd; 669 struct sysfs_dirent *sd;
660 670
661 for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) 671 for (sd = parent_sd->s_children; sd; sd = sd->s_sibling)
662 if (sysfs_type(sd) && !strcmp(sd->s_name, name)) 672 if (!strcmp(sd->s_name, name))
663 return sd; 673 return sd;
664 return NULL; 674 return NULL;
665} 675}
@@ -811,7 +821,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
811 while (*pos) { 821 while (*pos) {
812 struct sysfs_dirent *sd = *pos; 822 struct sysfs_dirent *sd = *pos;
813 823
814 if (sysfs_type(sd) && sysfs_type(sd) != SYSFS_DIR) 824 if (sysfs_type(sd) != SYSFS_DIR)
815 sysfs_remove_one(&acxt, sd); 825 sysfs_remove_one(&acxt, sd);
816 else 826 else
817 pos = &(*pos)->s_sibling; 827 pos = &(*pos)->s_sibling;
@@ -976,37 +986,6 @@ again:
976 return error; 986 return error;
977} 987}
978 988
979static int sysfs_dir_open(struct inode *inode, struct file *file)
980{
981 struct dentry * dentry = file->f_path.dentry;
982 struct sysfs_dirent * parent_sd = dentry->d_fsdata;
983 struct sysfs_dirent * sd;
984
985 sd = sysfs_new_dirent("_DIR_", 0, 0);
986 if (sd) {
987 mutex_lock(&sysfs_mutex);
988 sd->s_parent = sysfs_get(parent_sd);
989 sysfs_link_sibling(sd);
990 mutex_unlock(&sysfs_mutex);
991 }
992
993 file->private_data = sd;
994 return sd ? 0 : -ENOMEM;
995}
996
997static int sysfs_dir_close(struct inode *inode, struct file *file)
998{
999 struct sysfs_dirent * cursor = file->private_data;
1000
1001 mutex_lock(&sysfs_mutex);
1002 sysfs_unlink_sibling(cursor);
1003 mutex_unlock(&sysfs_mutex);
1004
1005 release_sysfs_dirent(cursor);
1006
1007 return 0;
1008}
1009
1010/* Relationship between s_mode and the DT_xxx types */ 989/* Relationship between s_mode and the DT_xxx types */
1011static inline unsigned char dt_type(struct sysfs_dirent *sd) 990static inline unsigned char dt_type(struct sysfs_dirent *sd)
1012{ 991{
@@ -1017,117 +996,51 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
1017{ 996{
1018 struct dentry *dentry = filp->f_path.dentry; 997 struct dentry *dentry = filp->f_path.dentry;
1019 struct sysfs_dirent * parent_sd = dentry->d_fsdata; 998 struct sysfs_dirent * parent_sd = dentry->d_fsdata;
1020 struct sysfs_dirent *cursor = filp->private_data; 999 struct sysfs_dirent *pos;
1021 struct sysfs_dirent **pos;
1022 ino_t ino; 1000 ino_t ino;
1023 int i = filp->f_pos;
1024 1001
1025 switch (i) { 1002 if (filp->f_pos == 0) {
1026 case 0: 1003 ino = parent_sd->s_ino;
1027 ino = parent_sd->s_ino; 1004 if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)
1028 if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
1029 break;
1030 filp->f_pos++; 1005 filp->f_pos++;
1031 i++; 1006 }
1032 /* fallthrough */ 1007 if (filp->f_pos == 1) {
1033 case 1: 1008 if (parent_sd->s_parent)
1034 if (parent_sd->s_parent) 1009 ino = parent_sd->s_parent->s_ino;
1035 ino = parent_sd->s_parent->s_ino; 1010 else
1036 else 1011 ino = parent_sd->s_ino;
1037 ino = parent_sd->s_ino; 1012 if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
1038 if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
1039 break;
1040 filp->f_pos++; 1013 filp->f_pos++;
1041 i++; 1014 }
1042 /* fallthrough */ 1015 if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) {
1043 default: 1016 mutex_lock(&sysfs_mutex);
1044 mutex_lock(&sysfs_mutex);
1045
1046 pos = &parent_sd->s_children;
1047 while (*pos != cursor)
1048 pos = &(*pos)->s_sibling;
1049
1050 /* unlink cursor */
1051 *pos = cursor->s_sibling;
1052
1053 if (filp->f_pos == 2)
1054 pos = &parent_sd->s_children;
1055
1056 for ( ; *pos; pos = &(*pos)->s_sibling) {
1057 struct sysfs_dirent *next = *pos;
1058 const char * name;
1059 int len;
1060
1061 if (!sysfs_type(next))
1062 continue;
1063
1064 name = next->s_name;
1065 len = strlen(name);
1066 ino = next->s_ino;
1067
1068 if (filldir(dirent, name, len, filp->f_pos, ino,
1069 dt_type(next)) < 0)
1070 break;
1071
1072 filp->f_pos++;
1073 }
1074 1017
1075 /* put cursor back in */ 1018 /* Skip the dentries we have already reported */
1076 cursor->s_sibling = *pos; 1019 pos = parent_sd->s_children;
1077 *pos = cursor; 1020 while (pos && (filp->f_pos > pos->s_ino))
1021 pos = pos->s_sibling;
1078 1022
1079 mutex_unlock(&sysfs_mutex); 1023 for ( ; pos; pos = pos->s_sibling) {
1080 } 1024 const char * name;
1081 return 0; 1025 int len;
1082}
1083 1026
1084static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) 1027 name = pos->s_name;
1085{ 1028 len = strlen(name);
1086 struct dentry * dentry = file->f_path.dentry; 1029 filp->f_pos = ino = pos->s_ino;
1087 1030
1088 switch (origin) { 1031 if (filldir(dirent, name, len, filp->f_pos, ino,
1089 case 1: 1032 dt_type(pos)) < 0)
1090 offset += file->f_pos;
1091 case 0:
1092 if (offset >= 0)
1093 break; 1033 break;
1094 default:
1095 return -EINVAL;
1096 }
1097 if (offset != file->f_pos) {
1098 mutex_lock(&sysfs_mutex);
1099
1100 file->f_pos = offset;
1101 if (file->f_pos >= 2) {
1102 struct sysfs_dirent *sd = dentry->d_fsdata;
1103 struct sysfs_dirent *cursor = file->private_data;
1104 struct sysfs_dirent **pos;
1105 loff_t n = file->f_pos - 2;
1106
1107 sysfs_unlink_sibling(cursor);
1108
1109 pos = &sd->s_children;
1110 while (n && *pos) {
1111 struct sysfs_dirent *next = *pos;
1112 if (sysfs_type(next))
1113 n--;
1114 pos = &(*pos)->s_sibling;
1115 }
1116
1117 cursor->s_sibling = *pos;
1118 *pos = cursor;
1119 } 1034 }
1120 1035 if (!pos)
1036 filp->f_pos = INT_MAX;
1121 mutex_unlock(&sysfs_mutex); 1037 mutex_unlock(&sysfs_mutex);
1122 } 1038 }
1123 1039 return 0;
1124 return offset;
1125} 1040}
1126 1041
1042
1127const struct file_operations sysfs_dir_operations = { 1043const struct file_operations sysfs_dir_operations = {
1128 .open = sysfs_dir_open,
1129 .release = sysfs_dir_close,
1130 .llseek = sysfs_dir_lseek,
1131 .read = generic_read_dir, 1044 .read = generic_read_dir,
1132 .readdir = sysfs_readdir, 1045 .readdir = sysfs_readdir,
1133}; 1046};