diff --git a/include/linux/unwind_deferred.h b/include/linux/unwind_deferred.h
index 1789c3624723..426e21457606 100644
--- a/include/linux/unwind_deferred.h
+++ b/include/linux/unwind_deferred.h
@@ -18,6 +18,14 @@ struct unwind_work {
#ifdef CONFIG_UNWIND_USER
+#define UNWIND_PENDING_BIT (BITS_PER_LONG - 1)
+#define UNWIND_PENDING BIT(UNWIND_PENDING_BIT)
+
+enum {
+ UNWIND_ALREADY_PENDING = 1,
+ UNWIND_ALREADY_EXECUTED = 2,
+};
+
void unwind_task_init(struct task_struct *task);
void unwind_task_free(struct task_struct *task);
@@ -29,9 +37,23 @@ void unwind_deferred_cancel(struct unwind_work *work);
static __always_inline void unwind_exit_to_user_mode(void)
{
- if (unlikely(current->unwind_info.cache))
- current->unwind_info.cache->nr_entries = 0;
- current->unwind_info.timestamp = 0;
+ struct unwind_task_info *info = ¤t->unwind_info;
+ unsigned long bits;
+
+ /* Was there any unwinding? */
+ if (likely(!info->unwind_mask))
+ return;
+
+ bits = info->unwind_mask;
+ do {
+ /* Is a task_work going to run again before going back */
+ if (bits & UNWIND_PENDING)
+ return;
+ } while (!try_cmpxchg(&info->unwind_mask, &bits, 0UL));
+
+ if (likely(info->cache))
+ info->cache->nr_entries = 0;
+ info->timestamp = 0;
}
#else /* !CONFIG_UNWIND_USER */diff --git a/include/linux/unwind_deferred_types.h b/include/linux/unwind_deferred_types.h
index 780b00c07208..f384e7f45783 100644
--- a/include/linux/unwind_deferred_types.h
+++ b/include/linux/unwind_deferred_types.h
@@ -13,7 +13,6 @@ struct unwind_task_info {
unsigned long unwind_mask;
u64 timestamp;
u64 nmi_timestamp;
- int pending;
};
#endif /* _LINUX_UNWIND_USER_DEFERRED_TYPES_H */diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c
index e44538a2be6c..8a6caaae04d3 100644
--- a/kernel/unwind/deferred.c
+++ b/kernel/unwind/deferred.c
@@ -19,6 +19,11 @@ static LIST_HEAD(callbacks);
static unsigned long unwind_mask;
DEFINE_STATIC_SRCU(unwind_srcu);
+static inline bool unwind_pending(struct unwind_task_info *info)
+{
+ return test_bit(UNWIND_PENDING_BIT, &info->unwind_mask);
+}
+
/*
* Read the task context timestamp, if this is the first caller then
* it will set the timestamp.@@ -107,14 +112,17 @@ static void unwind_deferred_task_work(struct callback_head *head)
struct unwind_task_info *info = container_of(head, struct unwind_task_info, work);
struct unwind_stacktrace trace;
struct unwind_work *work;
+ unsigned long bits;
u64 timestamp;
int idx;
- if (WARN_ON_ONCE(!info->pending))
+ if (WARN_ON_ONCE(!unwind_pending(info)))
return;
- /* Allow work to come in again */
- WRITE_ONCE(info->pending, 0);
+ /* Clear pending bit but make sure to have the current bits */
+ bits = READ_ONCE(info->unwind_mask);
+ while (!try_cmpxchg(&info->unwind_mask, &bits, bits & ~UNWIND_PENDING))
+ ;
/*
* From here on out, the callback must always be called, even if it's
@@ -137,10 +145,8 @@ static void unwind_deferred_task_work(struct callback_head *head)
idx = srcu_read_lock(&unwind_srcu);
list_for_each_entry_srcu(work, &callbacks, list,
srcu_read_lock_held(&unwind_srcu)) {
- if (info->unwind_mask & BIT(work->bit)) {
+ if (bits & BIT(work->bit))
work->func(work, &trace, timestamp);
- clear_bit(work->bit, &info->unwind_mask);
- }
}
srcu_read_unlock(&unwind_srcu, idx);
}@@ -167,10 +173,13 @@ static int unwind_deferred_request_nmi(struct unwind_work *work, u64 *timestamp)
inited_timestamp = true;
}
- if (info->unwind_mask & BIT(work->bit))
- return 1;
+ /* Is this already queued */
+ if (info->unwind_mask & BIT(work->bit)) {
+ return unwind_pending(info) ? UNWIND_ALREADY_PENDING :
+ UNWIND_ALREADY_EXECUTED;
+ }
- if (info->pending)
+ if (unwind_pending(info))
goto out;
ret = task_work_add(current, &info->work, TWA_NMI_CURRENT);@@ -185,9 +194,17 @@ static int unwind_deferred_request_nmi(struct unwind_work *work, u64 *timestamp)
return ret;
}
- info->pending = 1;
+ /*
+ * This is the first to set the PENDING_BIT, clear all others
+ * as any other bit has already had their callback called, and
+ * those callbacks should not be called again because of this
+ * new callback. If they request another callback, then they
+ * will get a new one.
+ */
+ info->unwind_mask = UNWIND_PENDING;
out:
- return test_and_set_bit(work->bit, &info->unwind_mask);
+ return test_and_set_bit(work->bit, &info->unwind_mask) ?
+ UNWIND_ALREADY_PENDING : 0;
}
/**
@@ -210,15 +227,17 @@ static int unwind_deferred_request_nmi(struct unwind_work *work, u64 *timestamp)
* it has already been previously called for the same entry context, it will be
* called again with the same stack trace and timestamp.
*
- * Return: 1 if the the callback was already queued.
- * 0 if the callback successfully was queued.
+ * Return: 0 if the callback successfully was queued.
+ * UNWIND_ALREADY_PENDING if the the callback was already queued.
+ * UNWIND_ALREADY_EXECUTED if the callback was already called
+ * (and will not be called again)
* Negative if there's an error.
* @timestamp holds the timestamp of the first request by any user
*/
int unwind_deferred_request(struct unwind_work *work, u64 *timestamp)
{
struct unwind_task_info *info = ¤t->unwind_info;
- int pending;
+ unsigned long old, bits;
int bit;
int ret;
@@ -240,28 +259,49 @@ int unwind_deferred_request(struct unwind_work *work, u64 *timestamp)
*timestamp = get_timestamp(info);
- /* This is already queued */
- if (info->unwind_mask & BIT(bit))
- return 1;
+ old = READ_ONCE(info->unwind_mask);
+
+ /* Is this already queued */
+ if (old & BIT(bit)) {
+ /*
+ * If pending is not set, it means this work's callback
+ * was already called.
+ */
+ return old & UNWIND_PENDING ? UNWIND_ALREADY_PENDING :
+ UNWIND_ALREADY_EXECUTED;
+ }
- /* callback already pending? */
- pending = READ_ONCE(info->pending);
- if (pending)
+ if (unwind_pending(info))
goto out;
- /* Claim the work unless an NMI just now swooped in to do so. */
- if (!try_cmpxchg(&info->pending, &pending, 1))
+ /*
+ * This is the first to enable another task_work for this task since
+ * the task entered the kernel, or had already called the callbacks.
+ * Set only the bit for this work and clear all others as they have
+ * already had their callbacks called, and do not need to call them
+ * again because of this work.
+ */
+ bits = UNWIND_PENDING | BIT(bit);
+
+ /*
+ * If the cmpxchg() fails, it means that an NMI came in and set
+ * the pending bit as well as cleared the other bits. Just
+ * jump to setting the bit for this work.
+ */
+ if (!try_cmpxchg(&info->unwind_mask, &old, bits))
goto out;
/* The work has been claimed, now schedule it. */
ret = task_work_add(current, &info->work, TWA_RESUME);
- if (WARN_ON_ONCE(ret)) {
- WRITE_ONCE(info->pending, 0);
- return ret;
- }
+
+ if (WARN_ON_ONCE(ret))
+ WRITE_ONCE(info->unwind_mask, 0);
+
+ return ret;
out:
- return test_and_set_bit(work->bit, &info->unwind_mask);
+ return test_and_set_bit(work->bit, &info->unwind_mask) ?
+ UNWIND_ALREADY_PENDING : 0;
}
void unwind_deferred_cancel(struct unwind_work *work)@@ -297,7 +337,7 @@ int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func)
guard(mutex)(&callback_mutex);
/* See if there's a bit in the mask available */
- if (unwind_mask == ~0UL)
+ if (unwind_mask == ~(UNWIND_PENDING))
return -EBUSY;
work->bit = ffz(unwind_mask);
--
2.47.2