At 01/06/2017 10:11 PM, David Sterba wrote:
Enabling btrfs tracepoints leads to instant crash, as reported. The wq
callbacks could free the memory and the tracepoints started to
dereference the members to get to fs_info.

The proposed fix https://marc.info/?l=linux-btrfs&m=148172436722606&w=2
removed the tracepoints but we could preserve them by passing only the
required data in a safe way.

Fixes: bc074524e123 ("btrfs: prefix fsid to all trace events")
CC: sta...@vger.kernel.org # 4.8+
Reported-by: Sebastian Andrzej Siewior <bige...@linutronix.de>
Signed-off-by: David Sterba <dste...@suse.com>

Reviewed-by: Qu Wenruo <quwen...@cn.fujitsu.com>

Thanks for the fix.
Qu

---
 fs/btrfs/async-thread.c      | 15 +++++++++++----
 include/trace/events/btrfs.h | 22 +++++++++++++---------
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c
index 63d197724519..ff0b0be92d61 100644
--- a/fs/btrfs/async-thread.c
+++ b/fs/btrfs/async-thread.c
@@ -273,6 +273,8 @@ static void run_ordered_work(struct __btrfs_workqueue *wq)
        unsigned long flags;

        while (1) {
+               void *wtag;
+
                spin_lock_irqsave(lock, flags);
                if (list_empty(list))
                        break;
@@ -299,11 +301,13 @@ static void run_ordered_work(struct __btrfs_workqueue *wq)
                spin_unlock_irqrestore(lock, flags);

                /*
-                * we don't want to call the ordered free functions
-                * with the lock held though
+                * We don't want to call the ordered free functions with the
+                * lock held though. Save the work as tag for the trace event,
+                * because the callback could free the structure.
                 */
+               wtag = work;
                work->ordered_free(work);
-               trace_btrfs_all_work_done(work);
+               trace_btrfs_all_work_done(wq->fs_info, wtag);
        }
        spin_unlock_irqrestore(lock, flags);
 }
@@ -311,6 +315,7 @@ static void run_ordered_work(struct __btrfs_workqueue *wq)
 static void normal_work_helper(struct btrfs_work *work)
 {
        struct __btrfs_workqueue *wq;
+       void *wtag;
        int need_order = 0;

        /*
@@ -324,6 +329,8 @@ static void normal_work_helper(struct btrfs_work *work)
        if (work->ordered_func)
                need_order = 1;
        wq = work->wq;
+       /* Safe for tracepoints in case work gets freed by the callback */
+       wtag = work;

        trace_btrfs_work_sched(work);
        thresh_exec_hook(wq);
@@ -333,7 +340,7 @@ static void normal_work_helper(struct btrfs_work *work)
                run_ordered_work(wq);
        }
        if (!need_order)
-               trace_btrfs_all_work_done(work);
+               trace_btrfs_all_work_done(wq->fs_info, wtag);
 }

 void btrfs_init_work(struct btrfs_work *work, btrfs_work_func_t uniq_func,
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index c14bed4ab097..b09225c77676 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -1157,22 +1157,26 @@ DECLARE_EVENT_CLASS(btrfs__work,
                   __entry->func, __entry->ordered_func, __entry->ordered_free)
 );

-/* For situiations that the work is freed */
+/*
+ * For situiations when the work is freed, we pass fs_info and a tag that that
+ * matches address of the work structure so it can be paired with the
+ * scheduling event.
+ */
 DECLARE_EVENT_CLASS(btrfs__work__done,

-       TP_PROTO(struct btrfs_work *work),
+       TP_PROTO(struct btrfs_fs_info *fs_info, void *wtag),

-       TP_ARGS(work),
+       TP_ARGS(fs_info, wtag),

        TP_STRUCT__entry_btrfs(
-               __field(        void *, work                    )
+               __field(        void *, wtag                    )
        ),

-       TP_fast_assign_btrfs(btrfs_work_owner(work),
-               __entry->work                = work;
+       TP_fast_assign_btrfs(fs_info,
+               __entry->wtag                = wtag;
        ),

-       TP_printk_btrfs("work->%p", __entry->work)
+       TP_printk_btrfs("work->%p", __entry->wtag)
 );

 DEFINE_EVENT(btrfs__work, btrfs_work_queued,
@@ -1191,9 +1195,9 @@ DEFINE_EVENT(btrfs__work, btrfs_work_sched,

 DEFINE_EVENT(btrfs__work__done, btrfs_all_work_done,

-       TP_PROTO(struct btrfs_work *work),
+       TP_PROTO(struct btrfs_fs_info *fs_info, void *wtag),

-       TP_ARGS(work)
+       TP_ARGS(fs_info, wtag)
 );

 DEFINE_EVENT(btrfs__work, btrfs_ordered_sched,



--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to