Skip to content

Commit 5bdcd5f

Browse files
akaherrostedt
authored andcommittedJul 30, 2023
eventfs: Implement removal of meta data from eventfs
When events are removed from tracefs, the eventfs must be aware of this. The eventfs_remove() removes the meta data from eventfs so that it will no longer create the files associated with that event. When an instance is removed from tracefs, eventfs_remove_events_dir() will remove and clean up the entire "events" directory. The helper function eventfs_remove_rec() is used to clean up and free the associated data from eventfs for both of the added functions. SRCU is used to protect the lists of meta data stored in the eventfs. The eventfs_mutex is used to protect the content of the items in the list. As lookups may be happening as deletions of events are made, the freeing of dentry/inodes and relative information is done after the SRCU grace period has passed. Link: https://lkml.kernel.org/r/1690568452-46553-9-git-send-email-akaher@vmware.com Signed-off-by: Ajay Kaher <akaher@vmware.com> Co-developed-by: Steven Rostedt (VMware) <rostedt@goodmis.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org> Tested-by: Ching-lin Yu <chinglinyu@google.com> Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202305030611.Kas747Ev-lkp@intel.com/ Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
1 parent a376007 commit 5bdcd5f

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed
 

‎fs/tracefs/event_inode.c

+143
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,14 @@ void eventfs_set_ef_status_free(struct dentry *dentry)
198198
if (!ef)
199199
goto out;
200200

201+
/*
202+
* If ef was freed, then the LSB bit is set for d_fsdata.
203+
* But this should not happen, as it should still have a
204+
* ref count that prevents it. Warn in case it does.
205+
*/
206+
if (WARN_ON_ONCE((unsigned long)ef & 1))
207+
goto out;
208+
201209
dentry->d_fsdata = NULL;
202210
ef->dentry = NULL;
203211
out:
@@ -656,3 +664,138 @@ int eventfs_add_file(const char *name, umode_t mode,
656664
mutex_unlock(&eventfs_mutex);
657665
return 0;
658666
}
667+
668+
static void free_ef(struct rcu_head *head)
669+
{
670+
struct eventfs_file *ef = container_of(head, struct eventfs_file, rcu);
671+
672+
kfree(ef->name);
673+
kfree(ef->ei);
674+
kfree(ef);
675+
}
676+
677+
/**
678+
* eventfs_remove_rec - remove eventfs dir or file from list
679+
* @ef: eventfs_file to be removed.
680+
* @head: to create list of eventfs_file to be deleted
681+
* @level: to check recursion depth
682+
*
683+
* The helper function eventfs_remove_rec() is used to clean up and free the
684+
* associated data from eventfs for both of the added functions.
685+
*/
686+
static void eventfs_remove_rec(struct eventfs_file *ef, struct list_head *head, int level)
687+
{
688+
struct eventfs_file *ef_child;
689+
690+
if (!ef)
691+
return;
692+
/*
693+
* Check recursion depth. It should never be greater than 3:
694+
* 0 - events/
695+
* 1 - events/group/
696+
* 2 - events/group/event/
697+
* 3 - events/group/event/file
698+
*/
699+
if (WARN_ON_ONCE(level > 3))
700+
return;
701+
702+
if (ef->ei) {
703+
/* search for nested folders or files */
704+
list_for_each_entry_srcu(ef_child, &ef->ei->e_top_files, list,
705+
lockdep_is_held(&eventfs_mutex)) {
706+
eventfs_remove_rec(ef_child, head, level + 1);
707+
}
708+
}
709+
710+
list_del_rcu(&ef->list);
711+
list_add_tail(&ef->del_list, head);
712+
}
713+
714+
/**
715+
* eventfs_remove - remove eventfs dir or file from list
716+
* @ef: eventfs_file to be removed.
717+
*
718+
* This function acquire the eventfs_mutex lock and call eventfs_remove_rec()
719+
*/
720+
void eventfs_remove(struct eventfs_file *ef)
721+
{
722+
struct eventfs_file *tmp;
723+
LIST_HEAD(ef_del_list);
724+
struct dentry *dentry_list = NULL;
725+
struct dentry *dentry;
726+
727+
if (!ef)
728+
return;
729+
730+
mutex_lock(&eventfs_mutex);
731+
eventfs_remove_rec(ef, &ef_del_list, 0);
732+
list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) {
733+
if (ef->dentry) {
734+
unsigned long ptr = (unsigned long)dentry_list;
735+
736+
/* Keep the dentry from being freed yet */
737+
dget(ef->dentry);
738+
739+
/*
740+
* Paranoid: The dget() above should prevent the dentry
741+
* from being freed and calling eventfs_set_ef_status_free().
742+
* But just in case, set the link list LSB pointer to 1
743+
* and have eventfs_set_ef_status_free() check that to
744+
* make sure that if it does happen, it will not think
745+
* the d_fsdata is an event_file.
746+
*
747+
* For this to work, no event_file should be allocated
748+
* on a odd space, as the ef should always be allocated
749+
* to be at least word aligned. Check for that too.
750+
*/
751+
WARN_ON_ONCE(ptr & 1);
752+
753+
ef->dentry->d_fsdata = (void *)(ptr | 1);
754+
dentry_list = ef->dentry;
755+
ef->dentry = NULL;
756+
}
757+
call_srcu(&eventfs_srcu, &ef->rcu, free_ef);
758+
}
759+
mutex_unlock(&eventfs_mutex);
760+
761+
while (dentry_list) {
762+
unsigned long ptr;
763+
764+
dentry = dentry_list;
765+
ptr = (unsigned long)dentry->d_fsdata & ~1UL;
766+
dentry_list = (struct dentry *)ptr;
767+
dentry->d_fsdata = NULL;
768+
d_invalidate(dentry);
769+
mutex_lock(&eventfs_mutex);
770+
/* dentry should now have at least a single reference */
771+
WARN_ONCE((int)d_count(dentry) < 1,
772+
"dentry %p less than one reference (%d) after invalidate\n",
773+
dentry, d_count(dentry));
774+
mutex_unlock(&eventfs_mutex);
775+
dput(dentry);
776+
}
777+
}
778+
779+
/**
780+
* eventfs_remove_events_dir - remove eventfs dir or file from list
781+
* @dentry: events's dentry to be removed.
782+
*
783+
* This function remove events main directory
784+
*/
785+
void eventfs_remove_events_dir(struct dentry *dentry)
786+
{
787+
struct tracefs_inode *ti;
788+
struct eventfs_inode *ei;
789+
790+
if (!dentry || !dentry->d_inode)
791+
return;
792+
793+
ti = get_tracefs(dentry->d_inode);
794+
if (!ti || !(ti->flags & TRACEFS_EVENT_INODE))
795+
return;
796+
797+
ei = ti->private;
798+
d_invalidate(dentry);
799+
dput(dentry);
800+
kfree(ei);
801+
}

‎include/linux/tracefs.h

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ int eventfs_add_events_file(const char *name, umode_t mode,
4040
struct dentry *parent, void *data,
4141
const struct file_operations *fops);
4242

43+
void eventfs_remove(struct eventfs_file *ef);
44+
45+
void eventfs_remove_events_dir(struct dentry *dentry);
46+
4347
struct dentry *tracefs_create_file(const char *name, umode_t mode,
4448
struct dentry *parent, void *data,
4549
const struct file_operations *fops);

0 commit comments

Comments
 (0)
Please sign in to comment.