diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2007-08-20 08:36:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-10-12 17:51:08 -0400 |
commit | 3efa65b92d832873ece62b42a4268c2515943977 (patch) | |
tree | c7d09674edfd683e614649564e9159066dfc1d40 /fs | |
parent | 94777e09180b6249d455baa2dbe34cf630e0c033 (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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/sysfs/dir.c | 175 |
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); | |||
33 | static void sysfs_link_sibling(struct sysfs_dirent *sd) | 33 | static 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 | ||
979 | static 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 | |||
997 | static 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 */ |
1011 | static inline unsigned char dt_type(struct sysfs_dirent *sd) | 990 | static 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 | ||
1084 | static 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 | |||
1127 | const struct file_operations sysfs_dir_operations = { | 1043 | const 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 | }; |