On Wed, Jan 31, 2024 at 01:49:22PM -0500, Steven Rostedt wrote:
@@ -329,32 +320,29 @@ static struct dentry *create_file(const char *name, umode_t mode, ti = get_tracefs(inode); ti->flags |= TRACEFS_EVENT_INODE;
- d_instantiate(dentry, inode);
- d_add(dentry, inode); fsnotify_create(dentry->d_parent->d_inode, dentry);
Seriously? stat(2), have it go away from dcache on memory pressure, lather, rinse, repeat... Won't *snotify get confused by the stream of creations of the same thing, with not a removal in sight?
- return eventfs_end_creating(dentry);
- return dentry;
}; /**
- create_dir - create a dir in the tracefs filesystem
- lookup_dir_entry - look up a dir in the tracefs filesystem
- @dentry: the directory to look up
- @ei: the eventfs_inode that represents the directory to create
- @parent: parent dentry for this file.
- This function will create a dentry for a directory represented by
*/
- This function will look up a dentry for a directory represented by
- a eventfs_inode.
-static struct dentry *create_dir(struct eventfs_inode *ei, struct dentry *parent) +static struct dentry *lookup_dir_entry(struct dentry *dentry,
- struct eventfs_inode *pei, struct eventfs_inode *ei)
{ struct tracefs_inode *ti;
- struct dentry *dentry; struct inode *inode;
- dentry = eventfs_start_creating(ei->name, parent);
- if (IS_ERR(dentry))
return dentry;
- inode = tracefs_get_inode(dentry->d_sb); if (unlikely(!inode))
return eventfs_failed_creating(dentry);
return ERR_PTR(-ENOMEM);
/* If the user updated the directory's attributes, use them */ update_inode_attr(dentry, inode, &ei->attr, @@ -371,11 +359,14 @@ static struct dentry *create_dir(struct eventfs_inode *ei, struct dentry *parent /* Only directories have ti->private set to an ei, not files */ ti->private = ei;
- dentry->d_fsdata = ei;
ei->dentry = dentry; // Remove me!
- inc_nlink(inode);
- d_instantiate(dentry, inode);
- d_add(dentry, inode); inc_nlink(dentry->d_parent->d_inode);
What will happen when that thing gets evicted from dcache, gets looked up again, and again, and...?
fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
Same re snotify confusion...
- return eventfs_end_creating(dentry);
- return dentry;
} static void free_ei(struct eventfs_inode *ei) @@ -425,7 +416,7 @@ void eventfs_set_ei_status_free(struct tracefs_inode *ti, struct dentry *dentry) } /**
- create_file_dentry - create a dentry for a file of an eventfs_inode
- lookup_file_dentry - create a dentry for a file of an eventfs_inode
- @ei: the eventfs_inode that the file will be created under
- @idx: the index into the d_children[] of the @ei
- @parent: The parent dentry of the created file.
@@ -438,157 +429,21 @@ void eventfs_set_ei_status_free(struct tracefs_inode *ti, struct dentry *dentry)
- address located at @e_dentry.
*/ static struct dentry * -create_file_dentry(struct eventfs_inode *ei, int idx,
struct dentry *parent, const char *name, umode_t mode, void *data,
+lookup_file_dentry(struct dentry *dentry,
struct eventfs_inode *ei, int idx,
const struct file_operations *fops)umode_t mode, void *data,
{ struct eventfs_attr *attr = NULL; struct dentry **e_dentry = &ei->d_children[idx];
- struct dentry *dentry;
- WARN_ON_ONCE(!inode_is_locked(parent->d_inode));
- mutex_lock(&eventfs_mutex);
- if (ei->is_freed) {
mutex_unlock(&eventfs_mutex);
return NULL;
- }
- /* If the e_dentry already has a dentry, use it */
- if (*e_dentry) {
dget(*e_dentry);
mutex_unlock(&eventfs_mutex);
return *e_dentry;
- }
- /* ei->entry_attrs are protected by SRCU */ if (ei->entry_attrs) attr = &ei->entry_attrs[idx];
- mutex_unlock(&eventfs_mutex);
- dentry = create_file(name, mode, attr, parent, data, fops);
- mutex_lock(&eventfs_mutex);
- if (IS_ERR_OR_NULL(dentry)) {
/*
* When the mutex was released, something else could have
* created the dentry for this e_dentry. In which case
* use that one.
*
* If ei->is_freed is set, the e_dentry is currently on its
* way to being freed, don't return it. If e_dentry is NULL
* it means it was already freed.
*/
if (ei->is_freed) {
dentry = NULL;
} else {
dentry = *e_dentry;
dget(dentry);
}
mutex_unlock(&eventfs_mutex);
return dentry;
- }
- if (!*e_dentry && !ei->is_freed) {
*e_dentry = dentry;
dentry->d_fsdata = ei;
- } else {
/*
* Should never happen unless we get here due to being freed.
* Otherwise it means two dentries exist with the same name.
*/
WARN_ON_ONCE(!ei->is_freed);
dentry = NULL;
- }
- mutex_unlock(&eventfs_mutex);
- return dentry;
-}
-/**
- eventfs_post_create_dir - post create dir routine
- @ei: eventfs_inode of recently created dir
- Map the meta-data of files within an eventfs dir to their parent dentry
- */
-static void eventfs_post_create_dir(struct eventfs_inode *ei) -{
- struct eventfs_inode *ei_child;
- lockdep_assert_held(&eventfs_mutex);
- /* srcu lock already held */
- /* fill parent-child relation */
- list_for_each_entry_srcu(ei_child, &ei->children, list,
srcu_read_lock_held(&eventfs_srcu)) {
ei_child->d_parent = ei->dentry;
- }
-}
-/**
- create_dir_dentry - Create a directory dentry for the eventfs_inode
- @pei: The eventfs_inode parent of ei.
- @ei: The eventfs_inode to create the directory for
- @parent: The dentry of the parent of this directory
- This creates and attaches a directory dentry to the eventfs_inode @ei.
- */
-static struct dentry * -create_dir_dentry(struct eventfs_inode *pei, struct eventfs_inode *ei,
struct dentry *parent)
-{
- struct dentry *dentry = NULL;
- WARN_ON_ONCE(!inode_is_locked(parent->d_inode));
- mutex_lock(&eventfs_mutex);
- if (pei->is_freed || ei->is_freed) {
mutex_unlock(&eventfs_mutex);
return NULL;
- }
- if (ei->dentry) {
/* If the eventfs_inode already has a dentry, use it */
dentry = ei->dentry;
dget(dentry);
mutex_unlock(&eventfs_mutex);
return dentry;
- }
- mutex_unlock(&eventfs_mutex);
- dentry->d_fsdata = ei; // NOTE: ei of _parent_
- lookup_file(dentry, mode, attr, data, fops);
- dentry = create_dir(ei, parent);
- mutex_lock(&eventfs_mutex);
- if (IS_ERR_OR_NULL(dentry) && !ei->is_freed) {
/*
* When the mutex was released, something else could have
* created the dentry for this e_dentry. In which case
* use that one.
*
* If ei->is_freed is set, the e_dentry is currently on its
* way to being freed.
*/
dentry = ei->dentry;
if (dentry)
dget(dentry);
mutex_unlock(&eventfs_mutex);
return dentry;
- }
- if (!ei->dentry && !ei->is_freed) {
ei->dentry = dentry;
eventfs_post_create_dir(ei);
dentry->d_fsdata = ei;
- } else {
/*
* Should never happen unless we get here due to being freed.
* Otherwise it means two dentries exist with the same name.
*/
WARN_ON_ONCE(!ei->is_freed);
dentry = NULL;
- }
- mutex_unlock(&eventfs_mutex);
- *e_dentry = dentry; // Remove me
return dentry; } @@ -607,79 +462,55 @@ static struct dentry *eventfs_root_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) {
- const struct file_operations *fops;
- const struct eventfs_entry *entry; struct eventfs_inode *ei_child; struct tracefs_inode *ti; struct eventfs_inode *ei;
- struct dentry *ei_dentry = NULL;
- struct dentry *ret = NULL;
- struct dentry *d; const char *name = dentry->d_name.name;
- umode_t mode;
- void *data;
- int idx;
- int i;
- int r;
- struct dentry *result = NULL;
ti = get_tracefs(dir); if (!(ti->flags & TRACEFS_EVENT_INODE))
Can that ever happen? I mean, why set ->i_op to something that has this for ->lookup() on a directory without TRACEFS_EVENT_INODE in its inode? It's not as if you ever removed that flag...
return NULL;
- /* Grab srcu to prevent the ei from going away */
- idx = srcu_read_lock(&eventfs_srcu);
return ERR_PTR(-EIO);
- /*
* Grab the eventfs_mutex to consistent value from ti->private.
* This s
mutex_lock(&eventfs_mutex);*/
- ei = READ_ONCE(ti->private);
- if (ei && !ei->is_freed)
ei_dentry = READ_ONCE(ei->dentry);
- mutex_unlock(&eventfs_mutex);
- if (!ei || !ei_dentry)
goto out;
- data = ei->data;
- ei = ti->private;
- if (!ei || ei->is_freed)
goto enoent;
- list_for_each_entry_srcu(ei_child, &ei->children, list,
srcu_read_lock_held(&eventfs_srcu)) {
- list_for_each_entry(ei_child, &ei->children, list) { if (strcmp(ei_child->name, name) != 0) continue;
ret = simple_lookup(dir, dentry, flags);
if (IS_ERR(ret))
goto out;
d = create_dir_dentry(ei, ei_child, ei_dentry);
dput(d);
if (ei_child->is_freed)
goto enoent;
Out of curiosity - can that happen now? You've got exclusion with eventfs_remove_rec(), so you shouldn't be able to catch the moment between setting ->is_freed and removal from the list...
goto out; }lookup_dir_entry(dentry, ei, ei_child);
- for (i = 0; i < ei->nr_entries; i++) {
entry = &ei->entries[i];
if (strcmp(name, entry->name) == 0) {
void *cdata = data;
mutex_lock(&eventfs_mutex);
/* If ei->is_freed, then the event itself may be too */
if (!ei->is_freed)
r = entry->callback(name, &mode, &cdata, &fops);
else
r = -1;
mutex_unlock(&eventfs_mutex);
if (r <= 0)
continue;
ret = simple_lookup(dir, dentry, flags);
if (IS_ERR(ret))
goto out;
d = create_file_dentry(ei, i, ei_dentry, name, mode, cdata, fops);
dput(d);
break;
}
- for (int i = 0; i < ei->nr_entries; i++) {
void *data;
umode_t mode;
const struct file_operations *fops;
const struct eventfs_entry *entry = &ei->entries[i];
if (strcmp(name, entry->name) != 0)
continue;
data = ei->data;
if (entry->callback(name, &mode, &data, &fops) <= 0)
goto enoent;
lookup_file_dentry(dentry, ei, i, mode, data, fops);
}goto out;
- enoent:
- /* Don't cache negative lookups, just return an error */
- result = ERR_PTR(-ENOENT);
Huh? Just return NULL and be done with that - you'll get an unhashed negative dentry and let the caller turn that into -ENOENT...
out:
- srcu_read_unlock(&eventfs_srcu, idx);
- return ret;
- mutex_unlock(&eventfs_mutex);
- return result;
} /*