Commit 3efa65b9 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Greg Kroah-Hartman

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: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 94777e09
...@@ -33,10 +33,20 @@ static DEFINE_IDA(sysfs_ino_ida); ...@@ -33,10 +33,20 @@ static DEFINE_IDA(sysfs_ino_ida);
static void sysfs_link_sibling(struct sysfs_dirent *sd) static void sysfs_link_sibling(struct sysfs_dirent *sd)
{ {
struct sysfs_dirent *parent_sd = sd->s_parent; struct sysfs_dirent *parent_sd = sd->s_parent;
struct sysfs_dirent **pos;
BUG_ON(sd->s_sibling); BUG_ON(sd->s_sibling);
sd->s_sibling = parent_sd->s_children;
parent_sd->s_children = sd; /* Store directory entries in order by ino. This allows
* readdir to properly restart without having to add a
* cursor into the s_children list.
*/
for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) {
if (sd->s_ino < (*pos)->s_ino)
break;
}
sd->s_sibling = *pos;
*pos = sd;
} }
/** /**
...@@ -659,7 +669,7 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, ...@@ -659,7 +669,7 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
struct sysfs_dirent *sd; struct sysfs_dirent *sd;
for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) for (sd = parent_sd->s_children; sd; sd = sd->s_sibling)
if (sysfs_type(sd) && !strcmp(sd->s_name, name)) if (!strcmp(sd->s_name, name))
return sd; return sd;
return NULL; return NULL;
} }
...@@ -811,7 +821,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) ...@@ -811,7 +821,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd)
while (*pos) { while (*pos) {
struct sysfs_dirent *sd = *pos; struct sysfs_dirent *sd = *pos;
if (sysfs_type(sd) && sysfs_type(sd) != SYSFS_DIR) if (sysfs_type(sd) != SYSFS_DIR)
sysfs_remove_one(&acxt, sd); sysfs_remove_one(&acxt, sd);
else else
pos = &(*pos)->s_sibling; pos = &(*pos)->s_sibling;
...@@ -976,37 +986,6 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) ...@@ -976,37 +986,6 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
return error; return error;
} }
static int sysfs_dir_open(struct inode *inode, struct file *file)
{
struct dentry * dentry = file->f_path.dentry;
struct sysfs_dirent * parent_sd = dentry->d_fsdata;
struct sysfs_dirent * sd;
sd = sysfs_new_dirent("_DIR_", 0, 0);
if (sd) {
mutex_lock(&sysfs_mutex);
sd->s_parent = sysfs_get(parent_sd);
sysfs_link_sibling(sd);
mutex_unlock(&sysfs_mutex);
}
file->private_data = sd;
return sd ? 0 : -ENOMEM;
}
static int sysfs_dir_close(struct inode *inode, struct file *file)
{
struct sysfs_dirent * cursor = file->private_data;
mutex_lock(&sysfs_mutex);
sysfs_unlink_sibling(cursor);
mutex_unlock(&sysfs_mutex);
release_sysfs_dirent(cursor);
return 0;
}
/* Relationship between s_mode and the DT_xxx types */ /* Relationship between s_mode and the DT_xxx types */
static inline unsigned char dt_type(struct sysfs_dirent *sd) static inline unsigned char dt_type(struct sysfs_dirent *sd)
{ {
...@@ -1017,117 +996,51 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) ...@@ -1017,117 +996,51 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
{ {
struct dentry *dentry = filp->f_path.dentry; struct dentry *dentry = filp->f_path.dentry;
struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent * parent_sd = dentry->d_fsdata;
struct sysfs_dirent *cursor = filp->private_data; struct sysfs_dirent *pos;
struct sysfs_dirent **pos;
ino_t ino; ino_t ino;
int i = filp->f_pos;
switch (i) { if (filp->f_pos == 0) {
case 0: ino = parent_sd->s_ino;
ino = parent_sd->s_ino; if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)
if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
break;
filp->f_pos++; filp->f_pos++;
i++; }
/* fallthrough */ if (filp->f_pos == 1) {
case 1: if (parent_sd->s_parent)
if (parent_sd->s_parent) ino = parent_sd->s_parent->s_ino;
ino = parent_sd->s_parent->s_ino; else
else ino = parent_sd->s_ino;
ino = parent_sd->s_ino; if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
break;
filp->f_pos++; filp->f_pos++;
i++; }
/* fallthrough */ if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) {
default: mutex_lock(&sysfs_mutex);
mutex_lock(&sysfs_mutex);
pos = &parent_sd->s_children;
while (*pos != cursor)
pos = &(*pos)->s_sibling;
/* unlink cursor */
*pos = cursor->s_sibling;
if (filp->f_pos == 2)
pos = &parent_sd->s_children;
for ( ; *pos; pos = &(*pos)->s_sibling) {
struct sysfs_dirent *next = *pos;
const char * name;
int len;
if (!sysfs_type(next))
continue;
name = next->s_name;
len = strlen(name);
ino = next->s_ino;
if (filldir(dirent, name, len, filp->f_pos, ino,
dt_type(next)) < 0)
break;
filp->f_pos++;
}
/* put cursor back in */ /* Skip the dentries we have already reported */
cursor->s_sibling = *pos; pos = parent_sd->s_children;
*pos = cursor; while (pos && (filp->f_pos > pos->s_ino))
pos = pos->s_sibling;
mutex_unlock(&sysfs_mutex); for ( ; pos; pos = pos->s_sibling) {
} const char * name;
return 0; int len;
}
static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) name = pos->s_name;
{ len = strlen(name);
struct dentry * dentry = file->f_path.dentry; filp->f_pos = ino = pos->s_ino;
switch (origin) { if (filldir(dirent, name, len, filp->f_pos, ino,
case 1: dt_type(pos)) < 0)
offset += file->f_pos;
case 0:
if (offset >= 0)
break; break;
default:
return -EINVAL;
}
if (offset != file->f_pos) {
mutex_lock(&sysfs_mutex);
file->f_pos = offset;
if (file->f_pos >= 2) {
struct sysfs_dirent *sd = dentry->d_fsdata;
struct sysfs_dirent *cursor = file->private_data;
struct sysfs_dirent **pos;
loff_t n = file->f_pos - 2;
sysfs_unlink_sibling(cursor);
pos = &sd->s_children;
while (n && *pos) {
struct sysfs_dirent *next = *pos;
if (sysfs_type(next))
n--;
pos = &(*pos)->s_sibling;
}
cursor->s_sibling = *pos;
*pos = cursor;
} }
if (!pos)
filp->f_pos = INT_MAX;
mutex_unlock(&sysfs_mutex); mutex_unlock(&sysfs_mutex);
} }
return 0;
return offset;
} }
const struct file_operations sysfs_dir_operations = { const struct file_operations sysfs_dir_operations = {
.open = sysfs_dir_open,
.release = sysfs_dir_close,
.llseek = sysfs_dir_lseek,
.read = generic_read_dir, .read = generic_read_dir,
.readdir = sysfs_readdir, .readdir = sysfs_readdir,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment