mirror of
https://github.com/linuxkit/linuxkit.git
synced 2026-04-05 04:17:06 +00:00
Merge pull request #3298 from TiejunChina/master-dev
Enable Preempt-RT Linux 4.19.x into Linuxkit
This commit is contained in:
@@ -263,14 +263,14 @@ $(eval $(call kernel,4.20.13,4.20.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.19.26,4.19.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.14.104,4.14.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.14.104,4.14.x,,-dbg))
|
||||
$(eval $(call kernel,4.14.87,4.14.x,-rt,))
|
||||
$(eval $(call kernel,4.19.25,4.19.x,-rt,))
|
||||
$(eval $(call kernel,4.9.161,4.9.x,$(EXTRA),$(DEBUG)))
|
||||
|
||||
else ifeq ($(ARCH),aarch64)
|
||||
$(eval $(call kernel,4.20.13,4.20.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.19.26,4.19.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.14.104,4.14.x,$(EXTRA),$(DEBUG)))
|
||||
$(eval $(call kernel,4.14.87,4.14.x,-rt,))
|
||||
$(eval $(call kernel,4.19.25,4.19.x,-rt,))
|
||||
|
||||
else ifeq ($(ARCH),s390x)
|
||||
$(eval $(call kernel,4.20.13,4.20.x,$(EXTRA),$(DEBUG)))
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
From cf77772195405adc1a0cd2bc304ed810e6420c73 Mon Sep 17 00:00:00 2001
|
||||
From: Boqun Feng <boqun.feng@gmail.com>
|
||||
Date: Fri, 9 Mar 2018 14:56:28 +0800
|
||||
Subject: [PATCH 001/450] rtmutex: Make rt_mutex_futex_unlock() safe for
|
||||
irq-off callsites
|
||||
|
||||
Upstream commit 6b0ef92fee2a3189eba6d6b827b247cb4f6da7e9
|
||||
|
||||
When running rcutorture with TREE03 config, CONFIG_PROVE_LOCKING=y, and
|
||||
kernel cmdline argument "rcutorture.gp_exp=1", lockdep reports a
|
||||
HARDIRQ-safe->HARDIRQ-unsafe deadlock:
|
||||
|
||||
================================
|
||||
WARNING: inconsistent lock state
|
||||
4.16.0-rc4+ #1 Not tainted
|
||||
--------------------------------
|
||||
inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage.
|
||||
takes:
|
||||
__schedule+0xbe/0xaf0
|
||||
{IN-HARDIRQ-W} state was registered at:
|
||||
_raw_spin_lock+0x2a/0x40
|
||||
scheduler_tick+0x47/0xf0
|
||||
...
|
||||
other info that might help us debug this:
|
||||
Possible unsafe locking scenario:
|
||||
CPU0
|
||||
----
|
||||
lock(&rq->lock);
|
||||
<Interrupt>
|
||||
lock(&rq->lock);
|
||||
*** DEADLOCK ***
|
||||
1 lock held by rcu_torture_rea/724:
|
||||
rcu_torture_read_lock+0x0/0x70
|
||||
stack backtrace:
|
||||
CPU: 2 PID: 724 Comm: rcu_torture_rea Not tainted 4.16.0-rc4+ #1
|
||||
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014
|
||||
Call Trace:
|
||||
lock_acquire+0x90/0x200
|
||||
? __schedule+0xbe/0xaf0
|
||||
_raw_spin_lock+0x2a/0x40
|
||||
? __schedule+0xbe/0xaf0
|
||||
__schedule+0xbe/0xaf0
|
||||
preempt_schedule_irq+0x2f/0x60
|
||||
retint_kernel+0x1b/0x2d
|
||||
RIP: 0010:rcu_read_unlock_special+0x0/0x680
|
||||
? rcu_torture_read_unlock+0x60/0x60
|
||||
__rcu_read_unlock+0x64/0x70
|
||||
rcu_torture_read_unlock+0x17/0x60
|
||||
rcu_torture_reader+0x275/0x450
|
||||
? rcutorture_booster_init+0x110/0x110
|
||||
? rcu_torture_stall+0x230/0x230
|
||||
? kthread+0x10e/0x130
|
||||
kthread+0x10e/0x130
|
||||
? kthread_create_worker_on_cpu+0x70/0x70
|
||||
? call_usermodehelper_exec_async+0x11a/0x150
|
||||
ret_from_fork+0x3a/0x50
|
||||
|
||||
This happens with the following even sequence:
|
||||
|
||||
preempt_schedule_irq();
|
||||
local_irq_enable();
|
||||
__schedule():
|
||||
local_irq_disable(); // irq off
|
||||
...
|
||||
rcu_note_context_switch():
|
||||
rcu_note_preempt_context_switch():
|
||||
rcu_read_unlock_special():
|
||||
local_irq_save(flags);
|
||||
...
|
||||
raw_spin_unlock_irqrestore(...,flags); // irq remains off
|
||||
rt_mutex_futex_unlock():
|
||||
raw_spin_lock_irq();
|
||||
...
|
||||
raw_spin_unlock_irq(); // accidentally set irq on
|
||||
|
||||
<return to __schedule()>
|
||||
rq_lock():
|
||||
raw_spin_lock(); // acquiring rq->lock with irq on
|
||||
|
||||
which means rq->lock becomes a HARDIRQ-unsafe lock, which can cause
|
||||
deadlocks in scheduler code.
|
||||
|
||||
This problem was introduced by commit 02a7c234e540 ("rcu: Suppress
|
||||
lockdep false-positive ->boost_mtx complaints"). That brought the user
|
||||
of rt_mutex_futex_unlock() with irq off.
|
||||
|
||||
To fix this, replace the *lock_irq() in rt_mutex_futex_unlock() with
|
||||
*lock_irq{save,restore}() to make it safe to call rt_mutex_futex_unlock()
|
||||
with irq off.
|
||||
|
||||
Fixes: 02a7c234e540 ("rcu: Suppress lockdep false-positive ->boost_mtx complaints")
|
||||
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Cc: Peter Zijlstra <peterz@infradead.org>
|
||||
Cc: Lai Jiangshan <jiangshanlai@gmail.com>
|
||||
Cc: Steven Rostedt <rostedt@goodmis.org>
|
||||
Cc: Josh Triplett <josh@joshtriplett.org>
|
||||
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
|
||||
Cc: "Paul E . McKenney" <paulmck@linux.vnet.ibm.com>
|
||||
Link: https://lkml.kernel.org/r/20180309065630.8283-1-boqun.feng@gmail.com
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/locking/rtmutex.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
|
||||
index 4ad35718f123..456750bf9c95 100644
|
||||
--- a/kernel/locking/rtmutex.c
|
||||
+++ b/kernel/locking/rtmutex.c
|
||||
@@ -1637,11 +1637,12 @@ bool __sched __rt_mutex_futex_unlock(struct rt_mutex *lock,
|
||||
void __sched rt_mutex_futex_unlock(struct rt_mutex *lock)
|
||||
{
|
||||
DEFINE_WAKE_Q(wake_q);
|
||||
+ unsigned long flags;
|
||||
bool postunlock;
|
||||
|
||||
- raw_spin_lock_irq(&lock->wait_lock);
|
||||
+ raw_spin_lock_irqsave(&lock->wait_lock, flags);
|
||||
postunlock = __rt_mutex_futex_unlock(lock, &wake_q);
|
||||
- raw_spin_unlock_irq(&lock->wait_lock);
|
||||
+ raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
|
||||
|
||||
if (postunlock)
|
||||
rt_mutex_postunlock(&wake_q);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
From 80b97a7398475268f38b59179b7798da7576593b Mon Sep 17 00:00:00 2001
|
||||
From: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
|
||||
Date: Tue, 19 Sep 2017 15:36:42 -0700
|
||||
Subject: [PATCH 002/450] rcu: Suppress lockdep false-positive ->boost_mtx
|
||||
complaints
|
||||
|
||||
Upstream commit bcda31a2659497df39d6bedfbdf17498b4f4ac89
|
||||
|
||||
RCU priority boosting uses rt_mutex_init_proxy_locked() to initialize an
|
||||
rt_mutex structure in locked state held by some other task. When that
|
||||
other task releases it, lockdep complains (quite accurately, but a bit
|
||||
uselessly) that the other task never acquired it. This complaint can
|
||||
suppress other, more helpful, lockdep complaints, and in any case it is
|
||||
a false positive.
|
||||
|
||||
This commit therefore switches from rt_mutex_unlock() to
|
||||
rt_mutex_futex_unlock(), thereby avoiding the lockdep annotations.
|
||||
Of course, if lockdep ever learns about rt_mutex_init_proxy_locked(),
|
||||
addtional adjustments will be required.
|
||||
|
||||
Suggested-by: Peter Zijlstra <peterz@infradead.org>
|
||||
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/rcu/tree_plugin.h | 5 ++---
|
||||
1 file changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
|
||||
index 8b3102d22823..181e2487c8b8 100644
|
||||
--- a/kernel/rcu/tree_plugin.h
|
||||
+++ b/kernel/rcu/tree_plugin.h
|
||||
@@ -31,11 +31,10 @@
|
||||
#include <linux/smpboot.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
#include "../time/tick-internal.h"
|
||||
+#include "../locking/rtmutex_common.h"
|
||||
|
||||
#ifdef CONFIG_RCU_BOOST
|
||||
|
||||
-#include "../locking/rtmutex_common.h"
|
||||
-
|
||||
/*
|
||||
* Control variables for per-CPU and per-rcu_node kthreads. These
|
||||
* handle all flavors of RCU.
|
||||
@@ -530,7 +529,7 @@ void rcu_read_unlock_special(struct task_struct *t)
|
||||
|
||||
/* Unboost if we were boosted. */
|
||||
if (IS_ENABLED(CONFIG_RCU_BOOST) && drop_boost_mutex)
|
||||
- rt_mutex_unlock(&rnp->boost_mtx);
|
||||
+ rt_mutex_futex_unlock(&rnp->boost_mtx);
|
||||
|
||||
/*
|
||||
* If this was the last task on the expedited lists,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
From 88e1236f4d0412babbf59bdf44516d4d2326d093 Mon Sep 17 00:00:00 2001
|
||||
From: Mikulas Patocka <mpatocka@redhat.com>
|
||||
Date: Fri, 10 Nov 2017 12:29:34 -0500
|
||||
Subject: [PATCH 003/450] brd: remove unused brd_mutex
|
||||
|
||||
Upstream commit 15f7b41f70ddcca3b555bd0fdc7c8da7466b517e
|
||||
|
||||
Remove unused mutex brd_mutex. It is unused since the commit ff26956875c2
|
||||
("brd: remove support for BLKFLSBUF").
|
||||
|
||||
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
|
||||
Signed-off-by: Jens Axboe <axboe@kernel.dk>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/block/brd.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
|
||||
index 2d7178f7754e..c1cf87718c2e 100644
|
||||
--- a/drivers/block/brd.c
|
||||
+++ b/drivers/block/brd.c
|
||||
@@ -60,7 +60,6 @@ struct brd_device {
|
||||
/*
|
||||
* Look up and return a brd's page for a given sector.
|
||||
*/
|
||||
-static DEFINE_MUTEX(brd_mutex);
|
||||
static struct page *brd_lookup_page(struct brd_device *brd, sector_t sector)
|
||||
{
|
||||
pgoff_t idx;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
From caadf195c1da52ea6db6e657346eeac26afb68f3 Mon Sep 17 00:00:00 2001
|
||||
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||
Date: Fri, 8 Sep 2017 07:07:13 -0700
|
||||
Subject: [PATCH 004/450] KVM: arm/arm64: Remove redundant preemptible checks
|
||||
|
||||
Upstream commit 5a24575032971c5a9a4580417a791c427ebdb8e5
|
||||
|
||||
The __this_cpu_read() and __this_cpu_write() functions already implement
|
||||
checks for the required preemption levels when using
|
||||
CONFIG_DEBUG_PREEMPT which gives you nice error messages and such.
|
||||
Therefore there is no need to explicitly check this using a BUG_ON() in
|
||||
the code (which we don't do for other uses of per cpu variables either).
|
||||
|
||||
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
|
||||
Reviewed-by: Andre Przywara <andre.przywara@arm.com>
|
||||
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
virt/kvm/arm/arm.c | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
|
||||
index ed42b8cf6f5b..045aa39b14c1 100644
|
||||
--- a/virt/kvm/arm/arm.c
|
||||
+++ b/virt/kvm/arm/arm.c
|
||||
@@ -69,7 +69,6 @@ static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
|
||||
|
||||
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
- BUG_ON(preemptible());
|
||||
__this_cpu_write(kvm_arm_running_vcpu, vcpu);
|
||||
}
|
||||
|
||||
@@ -79,7 +78,6 @@ static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
|
||||
*/
|
||||
struct kvm_vcpu *kvm_arm_get_running_vcpu(void)
|
||||
{
|
||||
- BUG_ON(preemptible());
|
||||
return __this_cpu_read(kvm_arm_running_vcpu);
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
From 0f961ceb5f56e333087f3ff78ba3f897f31a6113 Mon Sep 17 00:00:00 2001
|
||||
From: Scott Wood <swood@redhat.com>
|
||||
Date: Sun, 21 Jan 2018 03:28:54 -0600
|
||||
Subject: [PATCH 005/450] iommu/amd: Use raw locks on atomic context paths
|
||||
|
||||
Upstream commit 27790398c2aed917828dc3c6f81240d57f1584c9
|
||||
|
||||
Several functions in this driver are called from atomic context,
|
||||
and thus raw locks must be used in order to be safe on PREEMPT_RT.
|
||||
|
||||
This includes paths that must wait for command completion, which is
|
||||
a potential PREEMPT_RT latency concern but not easily avoidable.
|
||||
|
||||
Signed-off-by: Scott Wood <swood@redhat.com>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 30 +++++++++++++++---------------
|
||||
drivers/iommu/amd_iommu_init.c | 2 +-
|
||||
drivers/iommu/amd_iommu_types.h | 4 ++--
|
||||
3 files changed, 18 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index efa6cd2500b9..131a881d033d 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -1062,9 +1062,9 @@ static int iommu_queue_command_sync(struct amd_iommu *iommu,
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
- spin_lock_irqsave(&iommu->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&iommu->lock, flags);
|
||||
ret = __iommu_queue_command_sync(iommu, cmd, sync);
|
||||
- spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1090,7 +1090,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
|
||||
|
||||
build_completion_wait(&cmd, (u64)&iommu->cmd_sem);
|
||||
|
||||
- spin_lock_irqsave(&iommu->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&iommu->lock, flags);
|
||||
|
||||
iommu->cmd_sem = 0;
|
||||
|
||||
@@ -1101,7 +1101,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu)
|
||||
ret = wait_on_sem(&iommu->cmd_sem);
|
||||
|
||||
out_unlock:
|
||||
- spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -3626,7 +3626,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
|
||||
goto out_unlock;
|
||||
|
||||
/* Initialize table spin-lock */
|
||||
- spin_lock_init(&table->lock);
|
||||
+ raw_spin_lock_init(&table->lock);
|
||||
|
||||
if (ioapic)
|
||||
/* Keep the first 32 indexes free for IOAPIC interrupts */
|
||||
@@ -3685,7 +3685,7 @@ static int alloc_irq_index(u16 devid, int count)
|
||||
if (!table)
|
||||
return -ENODEV;
|
||||
|
||||
- spin_lock_irqsave(&table->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&table->lock, flags);
|
||||
|
||||
/* Scan table for free entries */
|
||||
for (c = 0, index = table->min_index;
|
||||
@@ -3708,7 +3708,7 @@ static int alloc_irq_index(u16 devid, int count)
|
||||
index = -ENOSPC;
|
||||
|
||||
out:
|
||||
- spin_unlock_irqrestore(&table->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
return index;
|
||||
}
|
||||
@@ -3729,7 +3729,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte,
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
- spin_lock_irqsave(&table->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&table->lock, flags);
|
||||
|
||||
entry = (struct irte_ga *)table->table;
|
||||
entry = &entry[index];
|
||||
@@ -3740,7 +3740,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte,
|
||||
if (data)
|
||||
data->ref = entry;
|
||||
|
||||
- spin_unlock_irqrestore(&table->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
@@ -3762,9 +3762,9 @@ static int modify_irte(u16 devid, int index, union irte *irte)
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
- spin_lock_irqsave(&table->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&table->lock, flags);
|
||||
table->table[index] = irte->val;
|
||||
- spin_unlock_irqrestore(&table->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
@@ -3786,9 +3786,9 @@ static void free_irte(u16 devid, int index)
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
- spin_lock_irqsave(&table->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&table->lock, flags);
|
||||
iommu->irte_ops->clear_allocated(table, index);
|
||||
- spin_unlock_irqrestore(&table->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
@@ -4367,7 +4367,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
if (!irt)
|
||||
return -ENODEV;
|
||||
|
||||
- spin_lock_irqsave(&irt->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&irt->lock, flags);
|
||||
|
||||
if (ref->lo.fields_vapic.guest_mode) {
|
||||
if (cpu >= 0)
|
||||
@@ -4376,7 +4376,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
barrier();
|
||||
}
|
||||
|
||||
- spin_unlock_irqrestore(&irt->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&irt->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
|
||||
index 6fe2d0346073..e3cd81b32a33 100644
|
||||
--- a/drivers/iommu/amd_iommu_init.c
|
||||
+++ b/drivers/iommu/amd_iommu_init.c
|
||||
@@ -1474,7 +1474,7 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
|
||||
{
|
||||
int ret;
|
||||
|
||||
- spin_lock_init(&iommu->lock);
|
||||
+ raw_spin_lock_init(&iommu->lock);
|
||||
|
||||
/* Add IOMMU to internal data structures */
|
||||
list_add_tail(&iommu->list, &amd_iommu_list);
|
||||
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
|
||||
index f6b24c7d8b70..7521745dc2a5 100644
|
||||
--- a/drivers/iommu/amd_iommu_types.h
|
||||
+++ b/drivers/iommu/amd_iommu_types.h
|
||||
@@ -406,7 +406,7 @@ extern bool amd_iommu_iotlb_sup;
|
||||
#define IRQ_TABLE_ALIGNMENT 128
|
||||
|
||||
struct irq_remap_table {
|
||||
- spinlock_t lock;
|
||||
+ raw_spinlock_t lock;
|
||||
unsigned min_index;
|
||||
u32 *table;
|
||||
};
|
||||
@@ -488,7 +488,7 @@ struct amd_iommu {
|
||||
int index;
|
||||
|
||||
/* locks the accesses to the hardware */
|
||||
- spinlock_t lock;
|
||||
+ raw_spinlock_t lock;
|
||||
|
||||
/* Pointer to PCI device of this IOMMU */
|
||||
struct pci_dev *dev;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
From f60d301f66278292bb819375e579980f52d01679 Mon Sep 17 00:00:00 2001
|
||||
From: Scott Wood <swood@redhat.com>
|
||||
Date: Sun, 28 Jan 2018 14:22:19 -0600
|
||||
Subject: [PATCH 006/450] iommu/amd: Don't use dev_data in
|
||||
irte_ga_set_affinity()
|
||||
|
||||
Upstream commit 01ee04badefd296eb7a4430497373be9b7b16783
|
||||
|
||||
search_dev_data() acquires a non-raw lock, which can't be done
|
||||
from atomic context on PREEMPT_RT. There is no need to look at
|
||||
dev_data because guest_mode should never be set if use_vapic is
|
||||
not set.
|
||||
|
||||
Signed-off-by: Scott Wood <swood@redhat.com>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 131a881d033d..24fc8ea4909a 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -3869,10 +3869,8 @@ static void irte_ga_set_affinity(void *entry, u16 devid, u16 index,
|
||||
u8 vector, u32 dest_apicid)
|
||||
{
|
||||
struct irte_ga *irte = (struct irte_ga *) entry;
|
||||
- struct iommu_dev_data *dev_data = search_dev_data(devid);
|
||||
|
||||
- if (!dev_data || !dev_data->use_vapic ||
|
||||
- !irte->lo.fields_remap.guest_mode) {
|
||||
+ if (!irte->lo.fields_remap.guest_mode) {
|
||||
irte->hi.fields.vector = vector;
|
||||
irte->lo.fields_remap.destination = dest_apicid;
|
||||
modify_irte_ga(devid, index, irte, NULL);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
From 75e9886d998becd42de359a6795f19d8aba1eeea Mon Sep 17 00:00:00 2001
|
||||
From: Scott Wood <swood@redhat.com>
|
||||
Date: Wed, 14 Feb 2018 17:36:28 -0600
|
||||
Subject: [PATCH 007/450] iommu/amd: Avoid locking get_irq_table() from atomic
|
||||
context
|
||||
|
||||
Upstream commit df42a04b15f19a842393dc98a84cbc52b1f8ed49
|
||||
|
||||
get_irq_table() previously acquired amd_iommu_devtable_lock which is not
|
||||
a raw lock, and thus cannot be acquired from atomic context on
|
||||
PREEMPT_RT. Many calls to modify_irte*() come from atomic context due to
|
||||
the IRQ desc->lock, as does amd_iommu_update_ga() due to the preemption
|
||||
disabling in vcpu_load/put().
|
||||
|
||||
The only difference between calling get_irq_table() and reading from
|
||||
irq_lookup_table[] directly, other than the lock acquisition and
|
||||
amd_iommu_rlookup_table[] check, is if the table entry is unpopulated,
|
||||
which should never happen when looking up a devid that came from an
|
||||
irq_2_irte struct, as get_irq_table() would have already been called on
|
||||
that devid during irq_remapping_alloc().
|
||||
|
||||
The lock acquisition is not needed in these cases because entries in
|
||||
irq_lookup_table[] never change once non-NULL -- nor would the
|
||||
amd_iommu_devtable_lock usage in get_irq_table() provide meaningful
|
||||
protection if they did, since it's released before using the looked up
|
||||
table in the get_irq_table() caller.
|
||||
|
||||
Rename the old get_irq_table() to alloc_irq_table(), and create a new
|
||||
lockless get_irq_table() to be used in non-allocating contexts that WARNs
|
||||
if it doesn't find what it's looking for.
|
||||
|
||||
Signed-off-by: Scott Wood <swood@redhat.com>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 29 ++++++++++++++++++++++-------
|
||||
1 file changed, 22 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 24fc8ea4909a..57c873ff05fa 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -3594,7 +3594,22 @@ static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
|
||||
amd_iommu_dev_table[devid].data[2] = dte;
|
||||
}
|
||||
|
||||
-static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
|
||||
+static struct irq_remap_table *get_irq_table(u16 devid)
|
||||
+{
|
||||
+ struct irq_remap_table *table;
|
||||
+
|
||||
+ if (WARN_ONCE(!amd_iommu_rlookup_table[devid],
|
||||
+ "%s: no iommu for devid %x\n", __func__, devid))
|
||||
+ return NULL;
|
||||
+
|
||||
+ table = irq_lookup_table[devid];
|
||||
+ if (WARN_ONCE(!table, "%s: no table for devid %x\n", __func__, devid))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return table;
|
||||
+}
|
||||
+
|
||||
+static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
{
|
||||
struct irq_remap_table *table = NULL;
|
||||
struct amd_iommu *iommu;
|
||||
@@ -3681,7 +3696,7 @@ static int alloc_irq_index(u16 devid, int count)
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
- table = get_irq_table(devid, false);
|
||||
+ table = alloc_irq_table(devid, false);
|
||||
if (!table)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -3725,7 +3740,7 @@ static int modify_irte_ga(u16 devid, int index, struct irte_ga *irte,
|
||||
if (iommu == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
- table = get_irq_table(devid, false);
|
||||
+ table = get_irq_table(devid);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -3758,7 +3773,7 @@ static int modify_irte(u16 devid, int index, union irte *irte)
|
||||
if (iommu == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
- table = get_irq_table(devid, false);
|
||||
+ table = get_irq_table(devid);
|
||||
if (!table)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -3782,7 +3797,7 @@ static void free_irte(u16 devid, int index)
|
||||
if (iommu == NULL)
|
||||
return;
|
||||
|
||||
- table = get_irq_table(devid, false);
|
||||
+ table = get_irq_table(devid);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
@@ -4100,7 +4115,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
return ret;
|
||||
|
||||
if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
|
||||
- if (get_irq_table(devid, true))
|
||||
+ if (alloc_irq_table(devid, true))
|
||||
index = info->ioapic_pin;
|
||||
else
|
||||
ret = -ENOMEM;
|
||||
@@ -4361,7 +4376,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
- irt = get_irq_table(devid, false);
|
||||
+ irt = get_irq_table(devid);
|
||||
if (!irt)
|
||||
return -ENODEV;
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
From ece88076b665589976cb6fc1e2fc07cc6e7c1ec4 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:34 +0100
|
||||
Subject: [PATCH 008/450] iommu/amd: Turn dev_data_list into a lock less list
|
||||
|
||||
Upstream commit 779da73273fc4c4c6f41579a95e4fb7880a1720e
|
||||
|
||||
alloc_dev_data() adds new items to dev_data_list and search_dev_data()
|
||||
is searching for items in this list. Both protect the access to the list
|
||||
with a spinlock.
|
||||
There is no need to navigate forth and back within the list and there is
|
||||
also no deleting of a specific item. This qualifies the list to become a
|
||||
lock less list and as part of this, the spinlock can be removed.
|
||||
With this change the ordering of those items within the list is changed:
|
||||
before the change new items were added to the end of the list, now they
|
||||
are added to the front. I don't think it matters but wanted to mention
|
||||
it.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 28 ++++++++++------------------
|
||||
drivers/iommu/amd_iommu_types.h | 2 +-
|
||||
2 files changed, 11 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 57c873ff05fa..50b6cab604aa 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -84,8 +84,7 @@
|
||||
static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
||||
|
||||
/* List of all available dev_data structures */
|
||||
-static LIST_HEAD(dev_data_list);
|
||||
-static DEFINE_SPINLOCK(dev_data_list_lock);
|
||||
+static LLIST_HEAD(dev_data_list);
|
||||
|
||||
LIST_HEAD(ioapic_map);
|
||||
LIST_HEAD(hpet_map);
|
||||
@@ -204,40 +203,33 @@ static struct dma_ops_domain* to_dma_ops_domain(struct protection_domain *domain
|
||||
static struct iommu_dev_data *alloc_dev_data(u16 devid)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
- unsigned long flags;
|
||||
|
||||
dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
|
||||
if (!dev_data)
|
||||
return NULL;
|
||||
|
||||
dev_data->devid = devid;
|
||||
-
|
||||
- spin_lock_irqsave(&dev_data_list_lock, flags);
|
||||
- list_add_tail(&dev_data->dev_data_list, &dev_data_list);
|
||||
- spin_unlock_irqrestore(&dev_data_list_lock, flags);
|
||||
-
|
||||
ratelimit_default_init(&dev_data->rs);
|
||||
|
||||
+ llist_add(&dev_data->dev_data_list, &dev_data_list);
|
||||
return dev_data;
|
||||
}
|
||||
|
||||
static struct iommu_dev_data *search_dev_data(u16 devid)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
- unsigned long flags;
|
||||
+ struct llist_node *node;
|
||||
+
|
||||
+ if (llist_empty(&dev_data_list))
|
||||
+ return NULL;
|
||||
|
||||
- spin_lock_irqsave(&dev_data_list_lock, flags);
|
||||
- list_for_each_entry(dev_data, &dev_data_list, dev_data_list) {
|
||||
+ node = dev_data_list.first;
|
||||
+ llist_for_each_entry(dev_data, node, dev_data_list) {
|
||||
if (dev_data->devid == devid)
|
||||
- goto out_unlock;
|
||||
+ return dev_data;
|
||||
}
|
||||
|
||||
- dev_data = NULL;
|
||||
-
|
||||
-out_unlock:
|
||||
- spin_unlock_irqrestore(&dev_data_list_lock, flags);
|
||||
-
|
||||
- return dev_data;
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
static int __last_alias(struct pci_dev *pdev, u16 alias, void *data)
|
||||
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
|
||||
index 7521745dc2a5..16b1404da58c 100644
|
||||
--- a/drivers/iommu/amd_iommu_types.h
|
||||
+++ b/drivers/iommu/amd_iommu_types.h
|
||||
@@ -625,7 +625,7 @@ struct devid_map {
|
||||
*/
|
||||
struct iommu_dev_data {
|
||||
struct list_head list; /* For domain->dev_list */
|
||||
- struct list_head dev_data_list; /* For global dev_data_list */
|
||||
+ struct llist_node dev_data_list; /* For global dev_data_list */
|
||||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
u16 devid; /* PCI Device ID */
|
||||
u16 alias; /* Alias Device ID */
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
From 5ef7777092fe740f67d1e304019e3bea28eb8fce Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:35 +0100
|
||||
Subject: [PATCH 009/450] iommu/amd: Split domain id out of
|
||||
amd_iommu_devtable_lock
|
||||
|
||||
Upstream commit 2bc00180890427dcc092b2f2b0d03c904bcade29
|
||||
|
||||
domain_id_alloc() and domain_id_free() is used for id management. Those
|
||||
two function share a bitmap (amd_iommu_pd_alloc_bitmap) and set/clear
|
||||
bits based on id allocation. There is no need to share this with
|
||||
amd_iommu_devtable_lock, it can use its own lock for this operation.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 12 +++++-------
|
||||
1 file changed, 5 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 50b6cab604aa..ccf9d1d08983 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -82,6 +82,7 @@
|
||||
#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38))
|
||||
|
||||
static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
||||
+static DEFINE_SPINLOCK(pd_bitmap_lock);
|
||||
|
||||
/* List of all available dev_data structures */
|
||||
static LLIST_HEAD(dev_data_list);
|
||||
@@ -1602,29 +1603,26 @@ static void del_domain_from_list(struct protection_domain *domain)
|
||||
|
||||
static u16 domain_id_alloc(void)
|
||||
{
|
||||
- unsigned long flags;
|
||||
int id;
|
||||
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock(&pd_bitmap_lock);
|
||||
id = find_first_zero_bit(amd_iommu_pd_alloc_bitmap, MAX_DOMAIN_ID);
|
||||
BUG_ON(id == 0);
|
||||
if (id > 0 && id < MAX_DOMAIN_ID)
|
||||
__set_bit(id, amd_iommu_pd_alloc_bitmap);
|
||||
else
|
||||
id = 0;
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock(&pd_bitmap_lock);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void domain_id_free(int id)
|
||||
{
|
||||
- unsigned long flags;
|
||||
-
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock(&pd_bitmap_lock);
|
||||
if (id > 0 && id < MAX_DOMAIN_ID)
|
||||
__clear_bit(id, amd_iommu_pd_alloc_bitmap);
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock(&pd_bitmap_lock);
|
||||
}
|
||||
|
||||
#define DEFINE_FREE_PT_FN(LVL, FN) \
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
From 5003ff93cc34975cd85c14248483548cd0635dc6 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:36 +0100
|
||||
Subject: [PATCH 010/450] iommu/amd: Split irq_lookup_table out of the
|
||||
amd_iommu_devtable_lock
|
||||
|
||||
Upstream commit ea6166f4b83e9cfba1c18f46a764d50045682fe5
|
||||
|
||||
The function get_irq_table() reads/writes irq_lookup_table while holding
|
||||
the amd_iommu_devtable_lock. It also modifies
|
||||
amd_iommu_dev_table[].data[2].
|
||||
set_dte_entry() is using amd_iommu_dev_table[].data[0|1] (under the
|
||||
domain->lock) so it should be okay. The access to the iommu is
|
||||
serialized with its own (iommu's) lock.
|
||||
|
||||
So split out get_irq_table() out of amd_iommu_devtable_lock's lock.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index ccf9d1d08983..1bb09f1e5cd1 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -83,6 +83,7 @@
|
||||
|
||||
static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
||||
static DEFINE_SPINLOCK(pd_bitmap_lock);
|
||||
+static DEFINE_SPINLOCK(iommu_table_lock);
|
||||
|
||||
/* List of all available dev_data structures */
|
||||
static LLIST_HEAD(dev_data_list);
|
||||
@@ -3606,7 +3607,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
unsigned long flags;
|
||||
u16 alias;
|
||||
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock_irqsave(&iommu_table_lock, flags);
|
||||
|
||||
iommu = amd_iommu_rlookup_table[devid];
|
||||
if (!iommu)
|
||||
@@ -3671,7 +3672,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
iommu_completion_wait(iommu);
|
||||
|
||||
out_unlock:
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock_irqrestore(&iommu_table_lock, flags);
|
||||
|
||||
return table;
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
From a3bd03fcc63c2806bee9b1b4da5d55a40a7d1c6c Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:37 +0100
|
||||
Subject: [PATCH 011/450] iommu/amd: Remove the special case from
|
||||
alloc_irq_table()
|
||||
|
||||
Upstream commit fde65dd3d3096e8f6ecc7bbe544eb91f4220772c
|
||||
|
||||
alloc_irq_table() has a special ioapic argument. If set then it will
|
||||
pre-allocate / reserve the first 32 indexes. The argument is only once
|
||||
true and it would make alloc_irq_table() a little simpler if we would
|
||||
extract the special bits to the caller.
|
||||
The caller of irq_remapping_alloc() is holding irq_domain_mutex so the
|
||||
initialization of iommu->irte_ops->set_allocated() should not race
|
||||
against other user.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 34 ++++++++++++++++++++--------------
|
||||
1 file changed, 20 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 1bb09f1e5cd1..f1017cf37eb3 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -3600,7 +3600,7 @@ static struct irq_remap_table *get_irq_table(u16 devid)
|
||||
return table;
|
||||
}
|
||||
|
||||
-static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
+static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
{
|
||||
struct irq_remap_table *table = NULL;
|
||||
struct amd_iommu *iommu;
|
||||
@@ -3634,10 +3634,6 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
/* Initialize table spin-lock */
|
||||
raw_spin_lock_init(&table->lock);
|
||||
|
||||
- if (ioapic)
|
||||
- /* Keep the first 32 indexes free for IOAPIC interrupts */
|
||||
- table->min_index = 32;
|
||||
-
|
||||
table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
|
||||
if (!table->table) {
|
||||
kfree(table);
|
||||
@@ -3652,12 +3648,6 @@ static struct irq_remap_table *alloc_irq_table(u16 devid, bool ioapic)
|
||||
memset(table->table, 0,
|
||||
(MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
|
||||
|
||||
- if (ioapic) {
|
||||
- int i;
|
||||
-
|
||||
- for (i = 0; i < 32; ++i)
|
||||
- iommu->irte_ops->set_allocated(table, i);
|
||||
- }
|
||||
|
||||
irq_lookup_table[devid] = table;
|
||||
set_dte_irq_entry(devid, table);
|
||||
@@ -3687,7 +3677,7 @@ static int alloc_irq_index(u16 devid, int count)
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
- table = alloc_irq_table(devid, false);
|
||||
+ table = alloc_irq_table(devid);
|
||||
if (!table)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -4106,10 +4096,26 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
return ret;
|
||||
|
||||
if (info->type == X86_IRQ_ALLOC_TYPE_IOAPIC) {
|
||||
- if (alloc_irq_table(devid, true))
|
||||
+ struct irq_remap_table *table;
|
||||
+ struct amd_iommu *iommu;
|
||||
+
|
||||
+ table = alloc_irq_table(devid);
|
||||
+ if (table) {
|
||||
+ if (!table->min_index) {
|
||||
+ /*
|
||||
+ * Keep the first 32 indexes free for IOAPIC
|
||||
+ * interrupts.
|
||||
+ */
|
||||
+ table->min_index = 32;
|
||||
+ iommu = amd_iommu_rlookup_table[devid];
|
||||
+ for (i = 0; i < 32; ++i)
|
||||
+ iommu->irte_ops->set_allocated(table, i);
|
||||
+ }
|
||||
+ WARN_ON(table->min_index != 32);
|
||||
index = info->ioapic_pin;
|
||||
- else
|
||||
+ } else {
|
||||
ret = -ENOMEM;
|
||||
+ }
|
||||
} else {
|
||||
index = alloc_irq_index(devid, nr_irqs);
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
From 340f3a55c46a7bf11cee5431d73ebbd1fa9081c0 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:38 +0100
|
||||
Subject: [PATCH 012/450] iommu/amd: Use `table' instead `irt' as variable name
|
||||
in amd_iommu_update_ga()
|
||||
|
||||
Upstream commit 4fde541c9dc114c5b448ad34b0286fe8b7c550f1
|
||||
|
||||
The variable of type struct irq_remap_table is always named `table'
|
||||
except in amd_iommu_update_ga() where it is called `irt'. Make it
|
||||
consistent and name it also `table'.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 10 +++++-----
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index f1017cf37eb3..561664b0e609 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -4359,7 +4359,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct amd_iommu *iommu;
|
||||
- struct irq_remap_table *irt;
|
||||
+ struct irq_remap_table *table;
|
||||
struct amd_ir_data *ir_data = (struct amd_ir_data *)data;
|
||||
int devid = ir_data->irq_2_irte.devid;
|
||||
struct irte_ga *entry = (struct irte_ga *) ir_data->entry;
|
||||
@@ -4373,11 +4373,11 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
if (!iommu)
|
||||
return -ENODEV;
|
||||
|
||||
- irt = get_irq_table(devid);
|
||||
- if (!irt)
|
||||
+ table = get_irq_table(devid);
|
||||
+ if (!table)
|
||||
return -ENODEV;
|
||||
|
||||
- raw_spin_lock_irqsave(&irt->lock, flags);
|
||||
+ raw_spin_lock_irqsave(&table->lock, flags);
|
||||
|
||||
if (ref->lo.fields_vapic.guest_mode) {
|
||||
if (cpu >= 0)
|
||||
@@ -4386,7 +4386,7 @@ int amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
||||
barrier();
|
||||
}
|
||||
|
||||
- raw_spin_unlock_irqrestore(&irt->lock, flags);
|
||||
+ raw_spin_unlock_irqrestore(&table->lock, flags);
|
||||
|
||||
iommu_flush_irt(iommu, devid);
|
||||
iommu_completion_wait(iommu);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
From 78b50efad4c03293a63d9b482f5c33f8186bfc9f Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:39 +0100
|
||||
Subject: [PATCH 013/450] iommu/amd: Factor out setting the remap table for a
|
||||
devid
|
||||
|
||||
Upstream commit 2fcc1e8ac4a8514c64f946178fc36c2e30e56a41
|
||||
|
||||
Setting the IRQ remap table for a specific devid (or its alias devid)
|
||||
includes three steps. Those three steps are always repeated each time
|
||||
this is done.
|
||||
Introduce a new helper function, move those steps there and use that
|
||||
function instead. The compiler can still decide if it is worth to
|
||||
inline.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 23 ++++++++++++-----------
|
||||
1 file changed, 12 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 561664b0e609..473ec78ba50c 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -3600,6 +3600,14 @@ static struct irq_remap_table *get_irq_table(u16 devid)
|
||||
return table;
|
||||
}
|
||||
|
||||
+static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
|
||||
+ struct irq_remap_table *table)
|
||||
+{
|
||||
+ irq_lookup_table[devid] = table;
|
||||
+ set_dte_irq_entry(devid, table);
|
||||
+ iommu_flush_dte(iommu, devid);
|
||||
+}
|
||||
+
|
||||
static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
{
|
||||
struct irq_remap_table *table = NULL;
|
||||
@@ -3620,9 +3628,7 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
alias = amd_iommu_alias_table[devid];
|
||||
table = irq_lookup_table[alias];
|
||||
if (table) {
|
||||
- irq_lookup_table[devid] = table;
|
||||
- set_dte_irq_entry(devid, table);
|
||||
- iommu_flush_dte(iommu, devid);
|
||||
+ set_remap_table_entry(iommu, devid, table);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -3649,14 +3655,9 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
(MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
|
||||
|
||||
|
||||
- irq_lookup_table[devid] = table;
|
||||
- set_dte_irq_entry(devid, table);
|
||||
- iommu_flush_dte(iommu, devid);
|
||||
- if (devid != alias) {
|
||||
- irq_lookup_table[alias] = table;
|
||||
- set_dte_irq_entry(alias, table);
|
||||
- iommu_flush_dte(iommu, alias);
|
||||
- }
|
||||
+ set_remap_table_entry(iommu, devid, table);
|
||||
+ if (devid != alias)
|
||||
+ set_remap_table_entry(iommu, alias, table);
|
||||
|
||||
out:
|
||||
iommu_completion_wait(iommu);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
From 0ef3ebf9be599ebc17441c220f837622a2ddeb79 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:40 +0100
|
||||
Subject: [PATCH 014/450] iommu/amd: Drop the lock while allocating new irq
|
||||
remap table
|
||||
|
||||
Upstream commit 993ca6e063a69a0c65ca42ed449b6bc1b3844151
|
||||
|
||||
The irq_remap_table is allocated while the iommu_table_lock is held with
|
||||
interrupts disabled.
|
||||
>From looking at the call sites, all callers are in the early device
|
||||
initialisation (apic_bsp_setup(), pci_enable_device(),
|
||||
pci_enable_msi()) so make sense to drop the lock which also enables
|
||||
interrupts and try to allocate that memory with GFP_KERNEL instead
|
||||
GFP_ATOMIC.
|
||||
|
||||
Since during the allocation the iommu_table_lock is dropped, we need to
|
||||
recheck if table exists after the lock has been reacquired. I *think*
|
||||
that it is impossible that the "devid" entry appears in irq_lookup_table
|
||||
while the lock is dropped since the same device can only be probed once.
|
||||
However I check for both cases, just to be sure.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 63 ++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 45 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 473ec78ba50c..c9e703260324 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -3600,6 +3600,30 @@ static struct irq_remap_table *get_irq_table(u16 devid)
|
||||
return table;
|
||||
}
|
||||
|
||||
+static struct irq_remap_table *__alloc_irq_table(void)
|
||||
+{
|
||||
+ struct irq_remap_table *table;
|
||||
+
|
||||
+ table = kzalloc(sizeof(*table), GFP_KERNEL);
|
||||
+ if (!table)
|
||||
+ return NULL;
|
||||
+
|
||||
+ table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_KERNEL);
|
||||
+ if (!table->table) {
|
||||
+ kfree(table);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ raw_spin_lock_init(&table->lock);
|
||||
+
|
||||
+ if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
|
||||
+ memset(table->table, 0,
|
||||
+ MAX_IRQS_PER_TABLE * sizeof(u32));
|
||||
+ else
|
||||
+ memset(table->table, 0,
|
||||
+ (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
|
||||
+ return table;
|
||||
+}
|
||||
+
|
||||
static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
|
||||
struct irq_remap_table *table)
|
||||
{
|
||||
@@ -3611,6 +3635,7 @@ static void set_remap_table_entry(struct amd_iommu *iommu, u16 devid,
|
||||
static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
{
|
||||
struct irq_remap_table *table = NULL;
|
||||
+ struct irq_remap_table *new_table = NULL;
|
||||
struct amd_iommu *iommu;
|
||||
unsigned long flags;
|
||||
u16 alias;
|
||||
@@ -3629,42 +3654,44 @@ static struct irq_remap_table *alloc_irq_table(u16 devid)
|
||||
table = irq_lookup_table[alias];
|
||||
if (table) {
|
||||
set_remap_table_entry(iommu, devid, table);
|
||||
- goto out;
|
||||
+ goto out_wait;
|
||||
}
|
||||
+ spin_unlock_irqrestore(&iommu_table_lock, flags);
|
||||
|
||||
/* Nothing there yet, allocate new irq remapping table */
|
||||
- table = kzalloc(sizeof(*table), GFP_ATOMIC);
|
||||
- if (!table)
|
||||
- goto out_unlock;
|
||||
+ new_table = __alloc_irq_table();
|
||||
+ if (!new_table)
|
||||
+ return NULL;
|
||||
|
||||
- /* Initialize table spin-lock */
|
||||
- raw_spin_lock_init(&table->lock);
|
||||
+ spin_lock_irqsave(&iommu_table_lock, flags);
|
||||
|
||||
- table->table = kmem_cache_alloc(amd_iommu_irq_cache, GFP_ATOMIC);
|
||||
- if (!table->table) {
|
||||
- kfree(table);
|
||||
- table = NULL;
|
||||
+ table = irq_lookup_table[devid];
|
||||
+ if (table)
|
||||
goto out_unlock;
|
||||
- }
|
||||
|
||||
- if (!AMD_IOMMU_GUEST_IR_GA(amd_iommu_guest_ir))
|
||||
- memset(table->table, 0,
|
||||
- MAX_IRQS_PER_TABLE * sizeof(u32));
|
||||
- else
|
||||
- memset(table->table, 0,
|
||||
- (MAX_IRQS_PER_TABLE * (sizeof(u64) * 2)));
|
||||
+ table = irq_lookup_table[alias];
|
||||
+ if (table) {
|
||||
+ set_remap_table_entry(iommu, devid, table);
|
||||
+ goto out_wait;
|
||||
+ }
|
||||
|
||||
+ table = new_table;
|
||||
+ new_table = NULL;
|
||||
|
||||
set_remap_table_entry(iommu, devid, table);
|
||||
if (devid != alias)
|
||||
set_remap_table_entry(iommu, alias, table);
|
||||
|
||||
-out:
|
||||
+out_wait:
|
||||
iommu_completion_wait(iommu);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&iommu_table_lock, flags);
|
||||
|
||||
+ if (new_table) {
|
||||
+ kmem_cache_free(amd_iommu_irq_cache, new_table->table);
|
||||
+ kfree(new_table);
|
||||
+ }
|
||||
return table;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
From 7febe26307d9a7f74f5e1509961b682017caaa31 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:41 +0100
|
||||
Subject: [PATCH 015/450] iommu/amd: Make amd_iommu_devtable_lock a spin_lock
|
||||
|
||||
Upstream commit 2cd1083d79a0a8c223af430ca97884c28a1e2fc0
|
||||
|
||||
Before commit 0bb6e243d7fb ("iommu/amd: Support IOMMU_DOMAIN_DMA type
|
||||
allocation") amd_iommu_devtable_lock had a read_lock() user but now
|
||||
there are none. In fact, after the mentioned commit we had only
|
||||
write_lock() user of the lock. Since there is no reason to keep it as
|
||||
writer lock, change its type to a spin_lock.
|
||||
I *think* that we might even be able to remove the lock because all its
|
||||
current user seem to have their own protection.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 14 +++++++-------
|
||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index c9e703260324..67008031537a 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -81,7 +81,7 @@
|
||||
*/
|
||||
#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38))
|
||||
|
||||
-static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
||||
+static DEFINE_SPINLOCK(amd_iommu_devtable_lock);
|
||||
static DEFINE_SPINLOCK(pd_bitmap_lock);
|
||||
static DEFINE_SPINLOCK(iommu_table_lock);
|
||||
|
||||
@@ -2092,9 +2092,9 @@ static int attach_device(struct device *dev,
|
||||
}
|
||||
|
||||
skip_ats_check:
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
ret = __attach_device(dev_data, domain);
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
|
||||
/*
|
||||
* We might boot into a crash-kernel here. The crashed kernel
|
||||
@@ -2144,9 +2144,9 @@ static void detach_device(struct device *dev)
|
||||
domain = dev_data->domain;
|
||||
|
||||
/* lock device table */
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
__detach_device(dev_data);
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
|
||||
if (!dev_is_pci(dev))
|
||||
return;
|
||||
@@ -2810,7 +2810,7 @@ static void cleanup_domain(struct protection_domain *domain)
|
||||
struct iommu_dev_data *entry;
|
||||
unsigned long flags;
|
||||
|
||||
- write_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_lock_irqsave(&amd_iommu_devtable_lock, flags);
|
||||
|
||||
while (!list_empty(&domain->dev_list)) {
|
||||
entry = list_first_entry(&domain->dev_list,
|
||||
@@ -2818,7 +2818,7 @@ static void cleanup_domain(struct protection_domain *domain)
|
||||
__detach_device(entry);
|
||||
}
|
||||
|
||||
- write_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
+ spin_unlock_irqrestore(&amd_iommu_devtable_lock, flags);
|
||||
}
|
||||
|
||||
static void protection_domain_free(struct protection_domain *domain)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
From 274e123364bc15e0fce26d4bc5f98793e6db18d8 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 22 Mar 2018 16:22:42 +0100
|
||||
Subject: [PATCH 016/450] iommu/amd: Return proper error code in
|
||||
irq_remapping_alloc()
|
||||
|
||||
Upstream commit 29d049be9438278c47253a74cf8d0ddf36bd5d68
|
||||
|
||||
In the unlikely case when alloc_irq_table() is not able to return a
|
||||
remap table then "ret" will be assigned with an error code. Later, the
|
||||
code checks `index' and if it is negative (which it is because it is
|
||||
initialized with `-1') and then then function properly aborts but
|
||||
returns `-1' instead `-ENOMEM' what was intended.
|
||||
In order to correct this, I assign -ENOMEM to index.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Signed-off-by: Joerg Roedel <jroedel@suse.de>
|
||||
---
|
||||
drivers/iommu/amd_iommu.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
|
||||
index 67008031537a..876947fcb4c6 100644
|
||||
--- a/drivers/iommu/amd_iommu.c
|
||||
+++ b/drivers/iommu/amd_iommu.c
|
||||
@@ -4100,7 +4100,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
struct amd_ir_data *data = NULL;
|
||||
struct irq_cfg *cfg;
|
||||
int i, ret, devid;
|
||||
- int index = -1;
|
||||
+ int index;
|
||||
|
||||
if (!info)
|
||||
return -EINVAL;
|
||||
@@ -4142,7 +4142,7 @@ static int irq_remapping_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
WARN_ON(table->min_index != 32);
|
||||
index = info->ioapic_pin;
|
||||
} else {
|
||||
- ret = -ENOMEM;
|
||||
+ index = -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
index = alloc_irq_index(devid, nr_irqs);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
From 7d3e8471f6d634b971ed483bf0fe0e3f426a799c Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:50 +0100
|
||||
Subject: [PATCH 017/450] timers: Use static keys for
|
||||
migrate_enable/nohz_active
|
||||
|
||||
The members migrate_enable and nohz_active in the timer/hrtimer per CPU
|
||||
bases have been introduced to avoid accessing global variables for these
|
||||
decisions.
|
||||
|
||||
Still that results in a (cache hot) load and conditional branch, which can
|
||||
be avoided by using static keys.
|
||||
|
||||
Implement it with static keys and optimize for the most critical case of
|
||||
high performance networking which tends to disable the timer migration
|
||||
functionality.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 4 --
|
||||
kernel/time/hrtimer.c | 17 +++-----
|
||||
kernel/time/tick-internal.h | 19 +++++----
|
||||
kernel/time/tick-sched.c | 2 +-
|
||||
kernel/time/timer.c | 83 +++++++++++++++++++------------------
|
||||
5 files changed, 61 insertions(+), 64 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 012c37fdb688..79b2a8d29d8c 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -153,8 +153,6 @@ enum hrtimer_base_type {
|
||||
* @cpu: cpu number
|
||||
* @active_bases: Bitfield to mark bases with active timers
|
||||
* @clock_was_set_seq: Sequence counter of clock was set events
|
||||
- * @migration_enabled: The migration of hrtimers to other cpus is enabled
|
||||
- * @nohz_active: The nohz functionality is enabled
|
||||
* @expires_next: absolute time of the next event which was scheduled
|
||||
* via clock_set_next_event()
|
||||
* @next_timer: Pointer to the first expiring timer
|
||||
@@ -178,8 +176,6 @@ struct hrtimer_cpu_base {
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set_seq;
|
||||
- bool migration_enabled;
|
||||
- bool nohz_active;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int in_hrtirq : 1,
|
||||
hres_active : 1,
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index d00e85ac10d6..883fef2926e9 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -178,23 +178,16 @@ hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base)
|
||||
#endif
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_NO_HZ_COMMON
|
||||
-static inline
|
||||
-struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base,
|
||||
- int pinned)
|
||||
-{
|
||||
- if (pinned || !base->migration_enabled)
|
||||
- return base;
|
||||
- return &per_cpu(hrtimer_bases, get_nohz_timer_target());
|
||||
-}
|
||||
-#else
|
||||
static inline
|
||||
struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base,
|
||||
int pinned)
|
||||
{
|
||||
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
|
||||
+ if (static_branch_unlikely(&timers_migration_enabled) && !pinned)
|
||||
+ return &per_cpu(hrtimer_bases, get_nohz_timer_target());
|
||||
+#endif
|
||||
return base;
|
||||
}
|
||||
-#endif
|
||||
|
||||
/*
|
||||
* We switch the timer base to a power-optimized selected CPU target,
|
||||
@@ -973,7 +966,7 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
* Kick to reschedule the next tick to handle the new timer
|
||||
* on dynticks target.
|
||||
*/
|
||||
- if (new_base->cpu_base->nohz_active)
|
||||
+ if (is_timers_nohz_active())
|
||||
wake_up_nohz_cpu(new_base->cpu_base->cpu);
|
||||
} else {
|
||||
hrtimer_reprogram(timer, new_base);
|
||||
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
|
||||
index f8e1845aa464..4ac74dff59f0 100644
|
||||
--- a/kernel/time/tick-internal.h
|
||||
+++ b/kernel/time/tick-internal.h
|
||||
@@ -150,14 +150,19 @@ static inline void tick_nohz_init(void) { }
|
||||
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
extern unsigned long tick_nohz_active;
|
||||
-#else
|
||||
+extern void timers_update_nohz(void);
|
||||
+extern struct static_key_false timers_nohz_active;
|
||||
+static inline bool is_timers_nohz_active(void)
|
||||
+{
|
||||
+ return static_branch_unlikely(&timers_nohz_active);
|
||||
+}
|
||||
+# ifdef CONFIG_SMP
|
||||
+extern struct static_key_false timers_migration_enabled;
|
||||
+# endif
|
||||
+#else /* CONFIG_NO_HZ_COMMON */
|
||||
+static inline void timers_update_nohz(void) { }
|
||||
#define tick_nohz_active (0)
|
||||
-#endif
|
||||
-
|
||||
-#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
|
||||
-extern void timers_update_migration(bool update_nohz);
|
||||
-#else
|
||||
-static inline void timers_update_migration(bool update_nohz) { }
|
||||
+static inline bool is_timers_nohz_active(void) { return false; }
|
||||
#endif
|
||||
|
||||
DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
|
||||
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
|
||||
index a8fa0a896b78..67e2b79b6a9b 100644
|
||||
--- a/kernel/time/tick-sched.c
|
||||
+++ b/kernel/time/tick-sched.c
|
||||
@@ -1132,7 +1132,7 @@ static inline void tick_nohz_activate(struct tick_sched *ts, int mode)
|
||||
ts->nohz_mode = mode;
|
||||
/* One update is enough */
|
||||
if (!test_and_set_bit(0, &tick_nohz_active))
|
||||
- timers_update_migration(true);
|
||||
+ timers_update_nohz();
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
|
||||
index f17c76a1a05f..9b644e70b660 100644
|
||||
--- a/kernel/time/timer.c
|
||||
+++ b/kernel/time/timer.c
|
||||
@@ -200,8 +200,6 @@ struct timer_base {
|
||||
unsigned long clk;
|
||||
unsigned long next_expiry;
|
||||
unsigned int cpu;
|
||||
- bool migration_enabled;
|
||||
- bool nohz_active;
|
||||
bool is_idle;
|
||||
bool must_forward_clk;
|
||||
DECLARE_BITMAP(pending_map, WHEEL_SIZE);
|
||||
@@ -210,45 +208,59 @@ struct timer_base {
|
||||
|
||||
static DEFINE_PER_CPU(struct timer_base, timer_bases[NR_BASES]);
|
||||
|
||||
-#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
|
||||
+#ifdef CONFIG_NO_HZ_COMMON
|
||||
+
|
||||
+DEFINE_STATIC_KEY_FALSE(timers_nohz_active);
|
||||
+static DEFINE_MUTEX(timer_keys_mutex);
|
||||
+
|
||||
+static void timer_update_keys(struct work_struct *work);
|
||||
+static DECLARE_WORK(timer_update_work, timer_update_keys);
|
||||
+
|
||||
+#ifdef CONFIG_SMP
|
||||
unsigned int sysctl_timer_migration = 1;
|
||||
|
||||
-void timers_update_migration(bool update_nohz)
|
||||
+DEFINE_STATIC_KEY_FALSE(timers_migration_enabled);
|
||||
+
|
||||
+static void timers_update_migration(void)
|
||||
{
|
||||
bool on = sysctl_timer_migration && tick_nohz_active;
|
||||
- unsigned int cpu;
|
||||
|
||||
- /* Avoid the loop, if nothing to update */
|
||||
- if (this_cpu_read(timer_bases[BASE_STD].migration_enabled) == on)
|
||||
- return;
|
||||
+ if (on)
|
||||
+ static_branch_enable(&timers_migration_enabled);
|
||||
+ else
|
||||
+ static_branch_disable(&timers_migration_enabled);
|
||||
+}
|
||||
+#else
|
||||
+static inline void timers_update_migration(void) { }
|
||||
+#endif /* !CONFIG_SMP */
|
||||
|
||||
- for_each_possible_cpu(cpu) {
|
||||
- per_cpu(timer_bases[BASE_STD].migration_enabled, cpu) = on;
|
||||
- per_cpu(timer_bases[BASE_DEF].migration_enabled, cpu) = on;
|
||||
- per_cpu(hrtimer_bases.migration_enabled, cpu) = on;
|
||||
- if (!update_nohz)
|
||||
- continue;
|
||||
- per_cpu(timer_bases[BASE_STD].nohz_active, cpu) = true;
|
||||
- per_cpu(timer_bases[BASE_DEF].nohz_active, cpu) = true;
|
||||
- per_cpu(hrtimer_bases.nohz_active, cpu) = true;
|
||||
- }
|
||||
+static void timer_update_keys(struct work_struct *work)
|
||||
+{
|
||||
+ mutex_lock(&timer_keys_mutex);
|
||||
+ timers_update_migration();
|
||||
+ static_branch_enable(&timers_nohz_active);
|
||||
+ mutex_unlock(&timer_keys_mutex);
|
||||
+}
|
||||
+
|
||||
+void timers_update_nohz(void)
|
||||
+{
|
||||
+ schedule_work(&timer_update_work);
|
||||
}
|
||||
|
||||
int timer_migration_handler(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp,
|
||||
loff_t *ppos)
|
||||
{
|
||||
- static DEFINE_MUTEX(mutex);
|
||||
int ret;
|
||||
|
||||
- mutex_lock(&mutex);
|
||||
+ mutex_lock(&timer_keys_mutex);
|
||||
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
|
||||
if (!ret && write)
|
||||
- timers_update_migration(false);
|
||||
- mutex_unlock(&mutex);
|
||||
+ timers_update_migration();
|
||||
+ mutex_unlock(&timer_keys_mutex);
|
||||
return ret;
|
||||
}
|
||||
-#endif
|
||||
+#endif /* NO_HZ_COMMON */
|
||||
|
||||
static unsigned long round_jiffies_common(unsigned long j, int cpu,
|
||||
bool force_up)
|
||||
@@ -534,7 +546,7 @@ __internal_add_timer(struct timer_base *base, struct timer_list *timer)
|
||||
static void
|
||||
trigger_dyntick_cpu(struct timer_base *base, struct timer_list *timer)
|
||||
{
|
||||
- if (!IS_ENABLED(CONFIG_NO_HZ_COMMON) || !base->nohz_active)
|
||||
+ if (!is_timers_nohz_active())
|
||||
return;
|
||||
|
||||
/*
|
||||
@@ -840,21 +852,20 @@ static inline struct timer_base *get_timer_base(u32 tflags)
|
||||
return get_timer_cpu_base(tflags, tflags & TIMER_CPUMASK);
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_NO_HZ_COMMON
|
||||
static inline struct timer_base *
|
||||
get_target_base(struct timer_base *base, unsigned tflags)
|
||||
{
|
||||
-#ifdef CONFIG_SMP
|
||||
- if ((tflags & TIMER_PINNED) || !base->migration_enabled)
|
||||
- return get_timer_this_cpu_base(tflags);
|
||||
- return get_timer_cpu_base(tflags, get_nohz_timer_target());
|
||||
-#else
|
||||
- return get_timer_this_cpu_base(tflags);
|
||||
+#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
|
||||
+ if (static_branch_unlikely(&timers_migration_enabled) &&
|
||||
+ !(tflags & TIMER_PINNED))
|
||||
+ return get_timer_cpu_base(tflags, get_nohz_timer_target());
|
||||
#endif
|
||||
+ return get_timer_this_cpu_base(tflags);
|
||||
}
|
||||
|
||||
static inline void forward_timer_base(struct timer_base *base)
|
||||
{
|
||||
+#ifdef CONFIG_NO_HZ_COMMON
|
||||
unsigned long jnow;
|
||||
|
||||
/*
|
||||
@@ -878,16 +889,8 @@ static inline void forward_timer_base(struct timer_base *base)
|
||||
base->clk = jnow;
|
||||
else
|
||||
base->clk = base->next_expiry;
|
||||
-}
|
||||
-#else
|
||||
-static inline struct timer_base *
|
||||
-get_target_base(struct timer_base *base, unsigned tflags)
|
||||
-{
|
||||
- return get_timer_this_cpu_base(tflags);
|
||||
-}
|
||||
-
|
||||
-static inline void forward_timer_base(struct timer_base *base) { }
|
||||
#endif
|
||||
+}
|
||||
|
||||
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
From 0032a44f1d407ce2c142b3493bb6f2bb60158896 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:51 +0100
|
||||
Subject: [PATCH 018/450] hrtimer: Correct blantanly wrong comment
|
||||
|
||||
The protection of a hrtimer which runs its callback against migration to a
|
||||
different CPU has nothing to do with hard interrupt context.
|
||||
|
||||
The protection against migration of a hrtimer running the expiry callback
|
||||
is the pointer in the cpu_base which holds a pointer to the currently
|
||||
running timer. This pointer is evaluated in the code which potentially
|
||||
switches the timer base and makes sure it's kept on the CPU on which the
|
||||
callback is running.
|
||||
|
||||
Reported-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 883fef2926e9..65543d31af32 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -1204,9 +1204,9 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
timer->is_rel = false;
|
||||
|
||||
/*
|
||||
- * Because we run timers from hardirq context, there is no chance
|
||||
- * they get migrated to another cpu, therefore its safe to unlock
|
||||
- * the timer base.
|
||||
+ * The timer is marked as running in the cpu base, so it is
|
||||
+ * protected against migration to a different CPU even if the lock
|
||||
+ * is dropped.
|
||||
*/
|
||||
raw_spin_unlock(&cpu_base->lock);
|
||||
trace_hrtimer_expire_entry(timer, now);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
From 514d5cebbbc72530f555551b1edbf7908bb3bf2d Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:52 +0100
|
||||
Subject: [PATCH 019/450] hrtimer: Fix kerneldoc for struct hrtimer_cpu_base
|
||||
|
||||
The sequence '/**' marks the start of a struct description. Add the
|
||||
missing second asterisk. While at it adapt the ordering of the struct
|
||||
members to the struct definition and document the purpose of
|
||||
expires_next more precisely.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 79b2a8d29d8c..b3a382be8db0 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -144,7 +144,7 @@ enum hrtimer_base_type {
|
||||
HRTIMER_MAX_CLOCK_BASES,
|
||||
};
|
||||
|
||||
-/*
|
||||
+/**
|
||||
* struct hrtimer_cpu_base - the per cpu clock bases
|
||||
* @lock: lock protecting the base and associated clock bases
|
||||
* and timers
|
||||
@@ -153,12 +153,12 @@ enum hrtimer_base_type {
|
||||
* @cpu: cpu number
|
||||
* @active_bases: Bitfield to mark bases with active timers
|
||||
* @clock_was_set_seq: Sequence counter of clock was set events
|
||||
- * @expires_next: absolute time of the next event which was scheduled
|
||||
- * via clock_set_next_event()
|
||||
- * @next_timer: Pointer to the first expiring timer
|
||||
* @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hres_active: State of high resolution mode
|
||||
* @hang_detected: The last hrtimer interrupt detected a hang
|
||||
+ * @expires_next: absolute time of the next event, is required for remote
|
||||
+ * hrtimer enqueue
|
||||
+ * @next_timer: Pointer to the first expiring timer
|
||||
* @nr_events: Total number of hrtimer interrupt events
|
||||
* @nr_retries: Total number of hrtimer interrupt retries
|
||||
* @nr_hangs: Total number of hrtimer interrupt hangs
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
From 88f3615b960b07d71c6fb2c6a14075be7e9099b4 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:53 +0100
|
||||
Subject: [PATCH 020/450] hrtimer: Cleanup clock argument in
|
||||
schedule_hrtimeout_range_clock()
|
||||
|
||||
schedule_hrtimeout_range_clock() uses an integer for the clock id
|
||||
instead of the predefined type "clockid_t". The ID of the clock is
|
||||
indicated in hrtimer code as clock_id. Therefore change the name of
|
||||
the variable as well to make it consistent.
|
||||
|
||||
While at it, clean up the description for the function parameters clock_id
|
||||
and mode. The clock modes and the clock ids are not restricted as the
|
||||
comment suggests. Fix the mode description as well for the callers of
|
||||
schedule_hrtimeout_range_clock().
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 2 +-
|
||||
kernel/time/hrtimer.c | 12 ++++++------
|
||||
2 files changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index b3a382be8db0..931ce9c89c93 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -462,7 +462,7 @@ extern int schedule_hrtimeout_range(ktime_t *expires, u64 delta,
|
||||
extern int schedule_hrtimeout_range_clock(ktime_t *expires,
|
||||
u64 delta,
|
||||
const enum hrtimer_mode mode,
|
||||
- int clock);
|
||||
+ clockid_t clock_id);
|
||||
extern int schedule_hrtimeout(ktime_t *expires, const enum hrtimer_mode mode);
|
||||
|
||||
/* Soft interrupt function to run the hrtimer queues: */
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 65543d31af32..790841b59433 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -1672,12 +1672,12 @@ void __init hrtimers_init(void)
|
||||
* schedule_hrtimeout_range_clock - sleep until timeout
|
||||
* @expires: timeout value (ktime_t)
|
||||
* @delta: slack in expires timeout (ktime_t)
|
||||
- * @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
|
||||
- * @clock: timer clock, CLOCK_MONOTONIC or CLOCK_REALTIME
|
||||
+ * @mode: timer mode
|
||||
+ * @clock_id: timer clock to be used
|
||||
*/
|
||||
int __sched
|
||||
schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
|
||||
- const enum hrtimer_mode mode, int clock)
|
||||
+ const enum hrtimer_mode mode, clockid_t clock_id)
|
||||
{
|
||||
struct hrtimer_sleeper t;
|
||||
|
||||
@@ -1698,7 +1698,7 @@ schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
- hrtimer_init_on_stack(&t.timer, clock, mode);
|
||||
+ hrtimer_init_on_stack(&t.timer, clock_id, mode);
|
||||
hrtimer_set_expires_range_ns(&t.timer, *expires, delta);
|
||||
|
||||
hrtimer_init_sleeper(&t, current);
|
||||
@@ -1720,7 +1720,7 @@ schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
|
||||
* schedule_hrtimeout_range - sleep until timeout
|
||||
* @expires: timeout value (ktime_t)
|
||||
* @delta: slack in expires timeout (ktime_t)
|
||||
- * @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
|
||||
+ * @mode: timer mode
|
||||
*
|
||||
* Make the current task sleep until the given expiry time has
|
||||
* elapsed. The routine will return immediately unless
|
||||
@@ -1759,7 +1759,7 @@ EXPORT_SYMBOL_GPL(schedule_hrtimeout_range);
|
||||
/**
|
||||
* schedule_hrtimeout - sleep until timeout
|
||||
* @expires: timeout value (ktime_t)
|
||||
- * @mode: timer mode, HRTIMER_MODE_ABS or HRTIMER_MODE_REL
|
||||
+ * @mode: timer mode
|
||||
*
|
||||
* Make the current task sleep until the given expiry time has
|
||||
* elapsed. The routine will return immediately unless
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
From 0a6e7eb400a68467d21993bd9ec87b43fc871411 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:54 +0100
|
||||
Subject: [PATCH 021/450] hrtimer: Fix hrtimer function description
|
||||
|
||||
The hrtimer_start[_range_ns]() starts a timer reliable on this CPU only
|
||||
when HRTIMER_MODE_PINNED is set. Furthermore the HRTIMER_MODE_PINNED mode
|
||||
is not considered, when a hrtimer is initialized.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 6 +++---
|
||||
kernel/time/hrtimer.c | 9 +++++----
|
||||
2 files changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 931ce9c89c93..4e6a8841dcbe 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -361,11 +361,11 @@ extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
u64 range_ns, const enum hrtimer_mode mode);
|
||||
|
||||
/**
|
||||
- * hrtimer_start - (re)start an hrtimer on the current CPU
|
||||
+ * hrtimer_start - (re)start an hrtimer
|
||||
* @timer: the timer to be added
|
||||
* @tim: expiry time
|
||||
- * @mode: expiry mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL)
|
||||
+ * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
+ * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
*/
|
||||
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,
|
||||
const enum hrtimer_mode mode)
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 790841b59433..6460aa2d9b25 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -928,12 +928,12 @@ static inline ktime_t hrtimer_update_lowres(struct hrtimer *timer, ktime_t tim,
|
||||
}
|
||||
|
||||
/**
|
||||
- * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU
|
||||
+ * hrtimer_start_range_ns - (re)start an hrtimer
|
||||
* @timer: the timer to be added
|
||||
* @tim: expiry time
|
||||
* @delta_ns: "slack" range for the timer
|
||||
- * @mode: expiry mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL)
|
||||
+ * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
+ * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
*/
|
||||
void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
u64 delta_ns, const enum hrtimer_mode mode)
|
||||
@@ -1116,7 +1116,8 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
* hrtimer_init - initialize a timer to the given clock
|
||||
* @timer: the timer to be initialized
|
||||
* @clock_id: the clock to be used
|
||||
- * @mode: timer mode abs/rel
|
||||
+ * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
+ * relative (HRTIMER_MODE_REL); pinned is not considered here!
|
||||
*/
|
||||
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
From c70a78a159599bbc711e49f8d1d44a978d7fd6da Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:56 +0100
|
||||
Subject: [PATCH 022/450] hrtimer: Cleanup hrtimer_mode enum
|
||||
|
||||
It's not obvious that the HRTIMER_MODE variants are bit combinations
|
||||
because all modes are hard coded constants.
|
||||
|
||||
Change it so the bit meanings are clear and use the symbols for creating
|
||||
modes which combine bits.
|
||||
|
||||
While at it get rid of the ugly tail comments.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 16 +++++++++++-----
|
||||
1 file changed, 11 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 4e6a8841dcbe..28f267cf2851 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -28,13 +28,19 @@ struct hrtimer_cpu_base;
|
||||
|
||||
/*
|
||||
* Mode arguments of xxx_hrtimer functions:
|
||||
+ *
|
||||
+ * HRTIMER_MODE_ABS - Time value is absolute
|
||||
+ * HRTIMER_MODE_REL - Time value is relative to now
|
||||
+ * HRTIMER_MODE_PINNED - Timer is bound to CPU (is only considered
|
||||
+ * when starting the timer)
|
||||
*/
|
||||
enum hrtimer_mode {
|
||||
- HRTIMER_MODE_ABS = 0x0, /* Time value is absolute */
|
||||
- HRTIMER_MODE_REL = 0x1, /* Time value is relative to now */
|
||||
- HRTIMER_MODE_PINNED = 0x02, /* Timer is bound to CPU */
|
||||
- HRTIMER_MODE_ABS_PINNED = 0x02,
|
||||
- HRTIMER_MODE_REL_PINNED = 0x03,
|
||||
+ HRTIMER_MODE_ABS = 0x00,
|
||||
+ HRTIMER_MODE_REL = 0x01,
|
||||
+ HRTIMER_MODE_PINNED = 0x02,
|
||||
+
|
||||
+ HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
|
||||
+ HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,
|
||||
};
|
||||
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
From 539a7faf8c0ee647ec65c88ab187f9200c8878cf Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:58 +0100
|
||||
Subject: [PATCH 023/450] tracing/hrtimer: Print hrtimer mode in hrtimer_start
|
||||
tracepoint
|
||||
|
||||
The hrtimer_start tracepoint lacks the mode information. The mode is
|
||||
important because consecutive starts can switch from ABS to REL or from
|
||||
PINNED to non PINNED.
|
||||
|
||||
Add the mode information.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/trace/events/timer.h | 13 ++++++++-----
|
||||
kernel/time/hrtimer.c | 16 +++++++++-------
|
||||
2 files changed, 17 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h
|
||||
index c6f728037c53..744b4310b24b 100644
|
||||
--- a/include/trace/events/timer.h
|
||||
+++ b/include/trace/events/timer.h
|
||||
@@ -186,15 +186,16 @@ TRACE_EVENT(hrtimer_init,
|
||||
*/
|
||||
TRACE_EVENT(hrtimer_start,
|
||||
|
||||
- TP_PROTO(struct hrtimer *hrtimer),
|
||||
+ TP_PROTO(struct hrtimer *hrtimer, enum hrtimer_mode mode),
|
||||
|
||||
- TP_ARGS(hrtimer),
|
||||
+ TP_ARGS(hrtimer, mode),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( void *, hrtimer )
|
||||
__field( void *, function )
|
||||
__field( s64, expires )
|
||||
__field( s64, softexpires )
|
||||
+ __field( enum hrtimer_mode, mode )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
@@ -202,12 +203,14 @@ TRACE_EVENT(hrtimer_start,
|
||||
__entry->function = hrtimer->function;
|
||||
__entry->expires = hrtimer_get_expires(hrtimer);
|
||||
__entry->softexpires = hrtimer_get_softexpires(hrtimer);
|
||||
+ __entry->mode = mode;
|
||||
),
|
||||
|
||||
- TP_printk("hrtimer=%p function=%pf expires=%llu softexpires=%llu",
|
||||
- __entry->hrtimer, __entry->function,
|
||||
+ TP_printk("hrtimer=%p function=%pf expires=%llu softexpires=%llu "
|
||||
+ "mode=%s", __entry->hrtimer, __entry->function,
|
||||
(unsigned long long) __entry->expires,
|
||||
- (unsigned long long) __entry->softexpires)
|
||||
+ (unsigned long long) __entry->softexpires,
|
||||
+ decode_hrtimer_mode(__entry->mode))
|
||||
);
|
||||
|
||||
/**
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 6460aa2d9b25..476fe683e8ed 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -435,10 +435,11 @@ debug_init(struct hrtimer *timer, clockid_t clockid,
|
||||
trace_hrtimer_init(timer, clockid, mode);
|
||||
}
|
||||
|
||||
-static inline void debug_activate(struct hrtimer *timer)
|
||||
+static inline void debug_activate(struct hrtimer *timer,
|
||||
+ enum hrtimer_mode mode)
|
||||
{
|
||||
debug_hrtimer_activate(timer);
|
||||
- trace_hrtimer_start(timer);
|
||||
+ trace_hrtimer_start(timer, mode);
|
||||
}
|
||||
|
||||
static inline void debug_deactivate(struct hrtimer *timer)
|
||||
@@ -832,9 +833,10 @@ EXPORT_SYMBOL_GPL(hrtimer_forward);
|
||||
* Returns 1 when the new timer is the leftmost timer in the tree.
|
||||
*/
|
||||
static int enqueue_hrtimer(struct hrtimer *timer,
|
||||
- struct hrtimer_clock_base *base)
|
||||
+ struct hrtimer_clock_base *base,
|
||||
+ enum hrtimer_mode mode)
|
||||
{
|
||||
- debug_activate(timer);
|
||||
+ debug_activate(timer, mode);
|
||||
|
||||
base->cpu_base->active_bases |= 1 << base->index;
|
||||
|
||||
@@ -957,7 +959,7 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
/* Switch the timer base, if necessary: */
|
||||
new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
|
||||
|
||||
- leftmost = enqueue_hrtimer(timer, new_base);
|
||||
+ leftmost = enqueue_hrtimer(timer, new_base, mode);
|
||||
if (!leftmost)
|
||||
goto unlock;
|
||||
|
||||
@@ -1226,7 +1228,7 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
*/
|
||||
if (restart != HRTIMER_NORESTART &&
|
||||
!(timer->state & HRTIMER_STATE_ENQUEUED))
|
||||
- enqueue_hrtimer(timer, base);
|
||||
+ enqueue_hrtimer(timer, base, HRTIMER_MODE_ABS);
|
||||
|
||||
/*
|
||||
* Separate the ->running assignment from the ->state assignment.
|
||||
@@ -1626,7 +1628,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
|
||||
* sort out already expired timers and reprogram the
|
||||
* event device.
|
||||
*/
|
||||
- enqueue_hrtimer(timer, new_base);
|
||||
+ enqueue_hrtimer(timer, new_base, HRTIMER_MODE_ABS);
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
From bb3a1b059ac7a6e2ada2d4045a8a18a125627e65 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:12:59 +0100
|
||||
Subject: [PATCH 024/450] hrtimer: Switch for loop to _ffs() evaluation
|
||||
|
||||
Looping over all clock bases to find active bits is suboptimal if not all
|
||||
bases are active.
|
||||
|
||||
Avoid this by converting it to a __ffs() evaluation. The functionallity is
|
||||
outsourced into an own function and is called via a macro as suggested by
|
||||
Peter Zijlstra.
|
||||
|
||||
Suggested-by: Peter Zijlstra <peterz@infradead.org>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 31 +++++++++++++++++++++----------
|
||||
1 file changed, 21 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 476fe683e8ed..85f9335d0d60 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -448,6 +448,23 @@ static inline void debug_deactivate(struct hrtimer *timer)
|
||||
trace_hrtimer_cancel(timer);
|
||||
}
|
||||
|
||||
+static struct hrtimer_clock_base *
|
||||
+__next_base(struct hrtimer_cpu_base *cpu_base, unsigned int *active)
|
||||
+{
|
||||
+ unsigned int idx;
|
||||
+
|
||||
+ if (!*active)
|
||||
+ return NULL;
|
||||
+
|
||||
+ idx = __ffs(*active);
|
||||
+ *active &= ~(1U << idx);
|
||||
+
|
||||
+ return &cpu_base->clock_base[idx];
|
||||
+}
|
||||
+
|
||||
+#define for_each_active_base(base, cpu_base, active) \
|
||||
+ while ((base = __next_base((cpu_base), &(active))))
|
||||
+
|
||||
#if defined(CONFIG_NO_HZ_COMMON) || defined(CONFIG_HIGH_RES_TIMERS)
|
||||
static inline void hrtimer_update_next_timer(struct hrtimer_cpu_base *cpu_base,
|
||||
struct hrtimer *timer)
|
||||
@@ -459,18 +476,15 @@ static inline void hrtimer_update_next_timer(struct hrtimer_cpu_base *cpu_base,
|
||||
|
||||
static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
{
|
||||
- struct hrtimer_clock_base *base = cpu_base->clock_base;
|
||||
+ struct hrtimer_clock_base *base;
|
||||
unsigned int active = cpu_base->active_bases;
|
||||
ktime_t expires, expires_next = KTIME_MAX;
|
||||
|
||||
hrtimer_update_next_timer(cpu_base, NULL);
|
||||
- for (; active; base++, active >>= 1) {
|
||||
+ for_each_active_base(base, cpu_base, active) {
|
||||
struct timerqueue_node *next;
|
||||
struct hrtimer *timer;
|
||||
|
||||
- if (!(active & 0x01))
|
||||
- continue;
|
||||
-
|
||||
next = timerqueue_getnext(&base->active);
|
||||
timer = container_of(next, struct hrtimer, node);
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
@@ -1245,16 +1259,13 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
|
||||
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
|
||||
{
|
||||
- struct hrtimer_clock_base *base = cpu_base->clock_base;
|
||||
+ struct hrtimer_clock_base *base;
|
||||
unsigned int active = cpu_base->active_bases;
|
||||
|
||||
- for (; active; base++, active >>= 1) {
|
||||
+ for_each_active_base(base, cpu_base, active) {
|
||||
struct timerqueue_node *node;
|
||||
ktime_t basenow;
|
||||
|
||||
- if (!(active & 0x01))
|
||||
- continue;
|
||||
-
|
||||
basenow = ktime_add(now, base->offset);
|
||||
|
||||
while ((node = timerqueue_getnext(&base->active))) {
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
From 06c6f558d3e8182d49a68dd3cafa56d771e0aa30 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:00 +0100
|
||||
Subject: [PATCH 025/450] hrtimer: Store running timer in hrtimer_clock_base
|
||||
|
||||
The pointer to the currently running timer is stored in hrtimer_cpu_base
|
||||
before the base lock is dropped and the callback is invoked.
|
||||
|
||||
This results in two levels of indirections and the upcoming support for
|
||||
softirq based hrtimer requires splitting the "running" storage into soft
|
||||
and hard irq context expiry.
|
||||
|
||||
Storing both in the cpu base would require conditionals in all code paths
|
||||
accessing that information.
|
||||
|
||||
It's possible to have a per clock base sequence count and running pointer
|
||||
without changing the semantics of the related mechanisms because the timer
|
||||
base pointer cannot be changed while a timer is running the callback.
|
||||
|
||||
Unfortunately this makes cpu_clock base larger than 32 bytes on 32bit
|
||||
kernels. Instead of having huge gaps due to alignment, remove the alignment
|
||||
and let the compiler pack cpu base for 32bit. The resulting cache access
|
||||
patterns are fortunately not really different from the current
|
||||
behaviour. On 64bit kernels the 64byte alignment stays and the behaviour is
|
||||
unchanged. This was determined by analyzing the resulting layout and
|
||||
looking at the number of cache lines involved for the frequently used
|
||||
clocks.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 20 +++++++++-----------
|
||||
kernel/time/hrtimer.c | 28 +++++++++++++---------------
|
||||
2 files changed, 22 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 28f267cf2851..1bae7b9f071d 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -118,9 +118,9 @@ struct hrtimer_sleeper {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
-# define HRTIMER_CLOCK_BASE_ALIGN 64
|
||||
+# define __hrtimer_clock_base_align ____cacheline_aligned
|
||||
#else
|
||||
-# define HRTIMER_CLOCK_BASE_ALIGN 32
|
||||
+# define __hrtimer_clock_base_align
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -129,18 +129,22 @@ struct hrtimer_sleeper {
|
||||
* @index: clock type index for per_cpu support when moving a
|
||||
* timer to a base on another cpu.
|
||||
* @clockid: clock id for per_cpu support
|
||||
+ * @seq: seqcount around __run_hrtimer
|
||||
+ * @running: pointer to the currently running hrtimer
|
||||
* @active: red black tree root node for the active timers
|
||||
* @get_time: function to retrieve the current time of the clock
|
||||
* @offset: offset of this clock to the monotonic base
|
||||
*/
|
||||
struct hrtimer_clock_base {
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
- int index;
|
||||
+ unsigned int index;
|
||||
clockid_t clockid;
|
||||
+ seqcount_t seq;
|
||||
+ struct hrtimer *running;
|
||||
struct timerqueue_head active;
|
||||
ktime_t (*get_time)(void);
|
||||
ktime_t offset;
|
||||
-} __attribute__((__aligned__(HRTIMER_CLOCK_BASE_ALIGN)));
|
||||
+} __hrtimer_clock_base_align;
|
||||
|
||||
enum hrtimer_base_type {
|
||||
HRTIMER_BASE_MONOTONIC,
|
||||
@@ -154,8 +158,6 @@ enum hrtimer_base_type {
|
||||
* struct hrtimer_cpu_base - the per cpu clock bases
|
||||
* @lock: lock protecting the base and associated clock bases
|
||||
* and timers
|
||||
- * @seq: seqcount around __run_hrtimer
|
||||
- * @running: pointer to the currently running hrtimer
|
||||
* @cpu: cpu number
|
||||
* @active_bases: Bitfield to mark bases with active timers
|
||||
* @clock_was_set_seq: Sequence counter of clock was set events
|
||||
@@ -177,8 +179,6 @@ enum hrtimer_base_type {
|
||||
*/
|
||||
struct hrtimer_cpu_base {
|
||||
raw_spinlock_t lock;
|
||||
- seqcount_t seq;
|
||||
- struct hrtimer *running;
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set_seq;
|
||||
@@ -198,8 +198,6 @@ struct hrtimer_cpu_base {
|
||||
|
||||
static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
|
||||
{
|
||||
- BUILD_BUG_ON(sizeof(struct hrtimer_clock_base) > HRTIMER_CLOCK_BASE_ALIGN);
|
||||
-
|
||||
timer->node.expires = time;
|
||||
timer->_softexpires = time;
|
||||
}
|
||||
@@ -424,7 +422,7 @@ static inline int hrtimer_is_queued(struct hrtimer *timer)
|
||||
*/
|
||||
static inline int hrtimer_callback_running(struct hrtimer *timer)
|
||||
{
|
||||
- return timer->base->cpu_base->running == timer;
|
||||
+ return timer->base->running == timer;
|
||||
}
|
||||
|
||||
/* Forward a hrtimer so it expires after now: */
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 85f9335d0d60..bedfc2865901 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -70,7 +70,6 @@
|
||||
DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
|
||||
{
|
||||
.lock = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases.lock),
|
||||
- .seq = SEQCNT_ZERO(hrtimer_bases.seq),
|
||||
.clock_base =
|
||||
{
|
||||
{
|
||||
@@ -118,7 +117,6 @@ static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
|
||||
* timer->base->cpu_base
|
||||
*/
|
||||
static struct hrtimer_cpu_base migration_cpu_base = {
|
||||
- .seq = SEQCNT_ZERO(migration_cpu_base),
|
||||
.clock_base = { { .cpu_base = &migration_cpu_base, }, },
|
||||
};
|
||||
|
||||
@@ -1152,19 +1150,19 @@ EXPORT_SYMBOL_GPL(hrtimer_init);
|
||||
*/
|
||||
bool hrtimer_active(const struct hrtimer *timer)
|
||||
{
|
||||
- struct hrtimer_cpu_base *cpu_base;
|
||||
+ struct hrtimer_clock_base *base;
|
||||
unsigned int seq;
|
||||
|
||||
do {
|
||||
- cpu_base = READ_ONCE(timer->base->cpu_base);
|
||||
- seq = raw_read_seqcount_begin(&cpu_base->seq);
|
||||
+ base = READ_ONCE(timer->base);
|
||||
+ seq = raw_read_seqcount_begin(&base->seq);
|
||||
|
||||
if (timer->state != HRTIMER_STATE_INACTIVE ||
|
||||
- cpu_base->running == timer)
|
||||
+ base->running == timer)
|
||||
return true;
|
||||
|
||||
- } while (read_seqcount_retry(&cpu_base->seq, seq) ||
|
||||
- cpu_base != READ_ONCE(timer->base->cpu_base));
|
||||
+ } while (read_seqcount_retry(&base->seq, seq) ||
|
||||
+ base != READ_ONCE(timer->base));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1198,16 +1196,16 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
lockdep_assert_held(&cpu_base->lock);
|
||||
|
||||
debug_deactivate(timer);
|
||||
- cpu_base->running = timer;
|
||||
+ base->running = timer;
|
||||
|
||||
/*
|
||||
* Separate the ->running assignment from the ->state assignment.
|
||||
*
|
||||
* As with a regular write barrier, this ensures the read side in
|
||||
- * hrtimer_active() cannot observe cpu_base->running == NULL &&
|
||||
+ * hrtimer_active() cannot observe base->running == NULL &&
|
||||
* timer->state == INACTIVE.
|
||||
*/
|
||||
- raw_write_seqcount_barrier(&cpu_base->seq);
|
||||
+ raw_write_seqcount_barrier(&base->seq);
|
||||
|
||||
__remove_hrtimer(timer, base, HRTIMER_STATE_INACTIVE, 0);
|
||||
fn = timer->function;
|
||||
@@ -1248,13 +1246,13 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
* Separate the ->running assignment from the ->state assignment.
|
||||
*
|
||||
* As with a regular write barrier, this ensures the read side in
|
||||
- * hrtimer_active() cannot observe cpu_base->running == NULL &&
|
||||
+ * hrtimer_active() cannot observe base->running.timer == NULL &&
|
||||
* timer->state == INACTIVE.
|
||||
*/
|
||||
- raw_write_seqcount_barrier(&cpu_base->seq);
|
||||
+ raw_write_seqcount_barrier(&base->seq);
|
||||
|
||||
- WARN_ON_ONCE(cpu_base->running != timer);
|
||||
- cpu_base->running = NULL;
|
||||
+ WARN_ON_ONCE(base->running != timer);
|
||||
+ base->running = NULL;
|
||||
}
|
||||
|
||||
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
From 848a963465a750a46dd6554f18ff5593d8b9cbda Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:01 +0100
|
||||
Subject: [PATCH 026/450] hrtimer: Make room in struct hrtimer_cpu_base
|
||||
|
||||
The upcoming softirq based hrtimers support requires an additional field in
|
||||
the hrtimer_cpu_base struct, which would grow the struct size beyond a
|
||||
cache line.
|
||||
|
||||
The struct members nr_retries and nr_hangs of hrtimer_cpu_base are solely
|
||||
used for diagnostic output and have no requirement to be unsigned int.
|
||||
|
||||
Make them unsigned short to create room for the new struct member. No
|
||||
functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 1bae7b9f071d..56e56bcb6f0f 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -189,8 +189,8 @@ struct hrtimer_cpu_base {
|
||||
ktime_t expires_next;
|
||||
struct hrtimer *next_timer;
|
||||
unsigned int nr_events;
|
||||
- unsigned int nr_retries;
|
||||
- unsigned int nr_hangs;
|
||||
+ unsigned short nr_retries;
|
||||
+ unsigned short nr_hangs;
|
||||
unsigned int max_hang_time;
|
||||
#endif
|
||||
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
From ae9cbcb83bcfc4a83965378438eca2a1ca78afc9 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:02 +0100
|
||||
Subject: [PATCH 027/450] hrtimer: Reduce conditional code (hres_active)
|
||||
|
||||
The hrtimer_cpu_base struct has the CONFIG_HIGH_RES_TIMERS conditional
|
||||
struct member hres_active. All related functions to this member are
|
||||
conditional as well.
|
||||
|
||||
There is no functional change, when the hres_active member is
|
||||
unconditional with all related functions and is set to zero during
|
||||
initialization.
|
||||
|
||||
The conditional code sections can be avoided by adding IS_ENABLED(HIGHRES)
|
||||
conditionals into common functions, which ensures dead code elimination.
|
||||
|
||||
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 20 ++++++++------------
|
||||
kernel/time/hrtimer.c | 31 +++++++++++++++----------------
|
||||
2 files changed, 23 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 56e56bcb6f0f..22627b3a33fe 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -161,8 +161,8 @@ enum hrtimer_base_type {
|
||||
* @cpu: cpu number
|
||||
* @active_bases: Bitfield to mark bases with active timers
|
||||
* @clock_was_set_seq: Sequence counter of clock was set events
|
||||
- * @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hres_active: State of high resolution mode
|
||||
+ * @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hang_detected: The last hrtimer interrupt detected a hang
|
||||
* @expires_next: absolute time of the next event, is required for remote
|
||||
* hrtimer enqueue
|
||||
@@ -182,9 +182,9 @@ struct hrtimer_cpu_base {
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set_seq;
|
||||
+ unsigned int hres_active : 1;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int in_hrtirq : 1,
|
||||
- hres_active : 1,
|
||||
hang_detected : 1;
|
||||
ktime_t expires_next;
|
||||
struct hrtimer *next_timer;
|
||||
@@ -266,16 +266,17 @@ static inline ktime_t hrtimer_cb_get_time(struct hrtimer *timer)
|
||||
return timer->base->get_time();
|
||||
}
|
||||
|
||||
+static inline int hrtimer_is_hres_active(struct hrtimer *timer)
|
||||
+{
|
||||
+ return IS_ENABLED(CONFIG_HIGH_RES_TIMERS) ?
|
||||
+ timer->base->cpu_base->hres_active : 0;
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
struct clock_event_device;
|
||||
|
||||
extern void hrtimer_interrupt(struct clock_event_device *dev);
|
||||
|
||||
-static inline int hrtimer_is_hres_active(struct hrtimer *timer)
|
||||
-{
|
||||
- return timer->base->cpu_base->hres_active;
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* The resolution of the clocks. The resolution value is returned in
|
||||
* the clock_getres() system call to give application programmers an
|
||||
@@ -298,11 +299,6 @@ extern unsigned int hrtimer_resolution;
|
||||
|
||||
#define hrtimer_resolution (unsigned int)LOW_RES_NSEC
|
||||
|
||||
-static inline int hrtimer_is_hres_active(struct hrtimer *timer)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static inline void clock_was_set_delayed(void) { }
|
||||
|
||||
#endif
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index bedfc2865901..7e0490143275 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -512,6 +512,20 @@ static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
|
||||
offs_real, offs_boot, offs_tai);
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Is the high resolution mode active ?
|
||||
+ */
|
||||
+static inline int __hrtimer_hres_active(struct hrtimer_cpu_base *cpu_base)
|
||||
+{
|
||||
+ return IS_ENABLED(CONFIG_HIGH_RES_TIMERS) ?
|
||||
+ cpu_base->hres_active : 0;
|
||||
+}
|
||||
+
|
||||
+static inline int hrtimer_hres_active(void)
|
||||
+{
|
||||
+ return __hrtimer_hres_active(this_cpu_ptr(&hrtimer_bases));
|
||||
+}
|
||||
+
|
||||
/* High resolution timer related functions */
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
||||
@@ -540,19 +554,6 @@ static inline int hrtimer_is_hres_enabled(void)
|
||||
return hrtimer_hres_enabled;
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Is the high resolution mode active ?
|
||||
- */
|
||||
-static inline int __hrtimer_hres_active(struct hrtimer_cpu_base *cpu_base)
|
||||
-{
|
||||
- return cpu_base->hres_active;
|
||||
-}
|
||||
-
|
||||
-static inline int hrtimer_hres_active(void)
|
||||
-{
|
||||
- return __hrtimer_hres_active(this_cpu_ptr(&hrtimer_bases));
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Reprogram the event source with checking both queues for the
|
||||
* next event
|
||||
@@ -662,7 +663,6 @@ static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
base->expires_next = KTIME_MAX;
|
||||
base->hang_detected = 0;
|
||||
- base->hres_active = 0;
|
||||
base->next_timer = NULL;
|
||||
}
|
||||
|
||||
@@ -722,8 +722,6 @@ void clock_was_set_delayed(void)
|
||||
|
||||
#else
|
||||
|
||||
-static inline int __hrtimer_hres_active(struct hrtimer_cpu_base *b) { return 0; }
|
||||
-static inline int hrtimer_hres_active(void) { return 0; }
|
||||
static inline int hrtimer_is_hres_enabled(void) { return 0; }
|
||||
static inline void hrtimer_switch_to_hres(void) { }
|
||||
static inline void
|
||||
@@ -1605,6 +1603,7 @@ int hrtimers_prepare_cpu(unsigned int cpu)
|
||||
|
||||
cpu_base->active_bases = 0;
|
||||
cpu_base->cpu = cpu;
|
||||
+ cpu_base->hres_active = 0;
|
||||
hrtimer_init_hres(cpu_base);
|
||||
return 0;
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
From b978729a14b5b64461840a95d386fe545dae0924 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:03 +0100
|
||||
Subject: [PATCH 028/450] hrtimer: Use accesor functions instead of direct
|
||||
access
|
||||
|
||||
__hrtimer_hres_active() is now available unconditionally. Replace the
|
||||
direct access to hrtimer_cpu_base.hres_active.
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 7e0490143275..85882d5da523 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -564,7 +564,7 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
{
|
||||
ktime_t expires_next;
|
||||
|
||||
- if (!cpu_base->hres_active)
|
||||
+ if (!__hrtimer_hres_active(cpu_base))
|
||||
return;
|
||||
|
||||
expires_next = __hrtimer_get_next_event(cpu_base);
|
||||
@@ -675,7 +675,7 @@ static void retrigger_next_event(void *arg)
|
||||
{
|
||||
struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases);
|
||||
|
||||
- if (!base->hres_active)
|
||||
+ if (!__hrtimer_hres_active(base))
|
||||
return;
|
||||
|
||||
raw_spin_lock(&base->lock);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
From b5cd9c9539b8a050c5c9806e54aa9b295215ba00 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:04 +0100
|
||||
Subject: [PATCH 029/450] hrtimer: Make the remote enqueue check unconditional
|
||||
|
||||
hrtimer_cpu_base.expires_next is used to cache the next event armed in the
|
||||
timer hardware. The value is used to check whether an hrtimer can be
|
||||
enqueued remotely. If the new hrtimer is expiring before expires_next, then
|
||||
remote enqueue is not possible as the remote hrtimer hardware cannot be
|
||||
accessed for reprogramming to an earlier expiry time.
|
||||
|
||||
The remote enqueue check is currently conditional on
|
||||
CONFIG_HIGH_RES_TIMERS=y and hrtimer_cpu_base.hres_active. There is no
|
||||
compelling reason to make this conditional.
|
||||
|
||||
Move hrtimer_cpu_base.expires_next out of the CONFIG_HIGH_RES_TIMERS=y
|
||||
guarded area and remove the conditionals in hrtimer_check_target().
|
||||
|
||||
The check is currently a NOOP for the CONFIG_HIGH_RES_TIMERS=n and the
|
||||
!hrtimer_cpu_base.hres_active case because in these cases nothing updates
|
||||
hrtimer_cpu_base.expires_next yet. This will be changed with later patches
|
||||
which further reduce the #ifdef zoo in this code.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 6 +++---
|
||||
kernel/time/hrtimer.c | 32 +++++++++-----------------------
|
||||
2 files changed, 12 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 22627b3a33fe..bb7270e8bc37 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -164,13 +164,13 @@ enum hrtimer_base_type {
|
||||
* @hres_active: State of high resolution mode
|
||||
* @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hang_detected: The last hrtimer interrupt detected a hang
|
||||
- * @expires_next: absolute time of the next event, is required for remote
|
||||
- * hrtimer enqueue
|
||||
* @next_timer: Pointer to the first expiring timer
|
||||
* @nr_events: Total number of hrtimer interrupt events
|
||||
* @nr_retries: Total number of hrtimer interrupt retries
|
||||
* @nr_hangs: Total number of hrtimer interrupt hangs
|
||||
* @max_hang_time: Maximum time spent in hrtimer_interrupt
|
||||
+ * @expires_next: absolute time of the next event, is required for remote
|
||||
+ * hrtimer enqueue
|
||||
* @clock_base: array of clock bases for this cpu
|
||||
*
|
||||
* Note: next_timer is just an optimization for __remove_hrtimer().
|
||||
@@ -186,13 +186,13 @@ struct hrtimer_cpu_base {
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int in_hrtirq : 1,
|
||||
hang_detected : 1;
|
||||
- ktime_t expires_next;
|
||||
struct hrtimer *next_timer;
|
||||
unsigned int nr_events;
|
||||
unsigned short nr_retries;
|
||||
unsigned short nr_hangs;
|
||||
unsigned int max_hang_time;
|
||||
#endif
|
||||
+ ktime_t expires_next;
|
||||
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
|
||||
} ____cacheline_aligned;
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 85882d5da523..b1016aabc73a 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -154,26 +154,21 @@ struct hrtimer_clock_base *lock_hrtimer_base(const struct hrtimer *timer,
|
||||
}
|
||||
|
||||
/*
|
||||
- * With HIGHRES=y we do not migrate the timer when it is expiring
|
||||
- * before the next event on the target cpu because we cannot reprogram
|
||||
- * the target cpu hardware and we would cause it to fire late.
|
||||
+ * We do not migrate the timer when it is expiring before the next
|
||||
+ * event on the target cpu. When high resolution is enabled, we cannot
|
||||
+ * reprogram the target cpu hardware and we would cause it to fire
|
||||
+ * late. To keep it simple, we handle the high resolution enabled and
|
||||
+ * disabled case similar.
|
||||
*
|
||||
* Called with cpu_base->lock of target cpu held.
|
||||
*/
|
||||
static int
|
||||
hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base)
|
||||
{
|
||||
-#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
ktime_t expires;
|
||||
|
||||
- if (!new_base->cpu_base->hres_active)
|
||||
- return 0;
|
||||
-
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), new_base->offset);
|
||||
return expires <= new_base->cpu_base->expires_next;
|
||||
-#else
|
||||
- return 0;
|
||||
-#endif
|
||||
}
|
||||
|
||||
static inline
|
||||
@@ -656,16 +651,6 @@ static void hrtimer_reprogram(struct hrtimer *timer,
|
||||
tick_program_event(expires, 1);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Initialize the high resolution related parts of cpu_base
|
||||
- */
|
||||
-static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base)
|
||||
-{
|
||||
- base->expires_next = KTIME_MAX;
|
||||
- base->hang_detected = 0;
|
||||
- base->next_timer = NULL;
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Retrigger next event is called after clock was set
|
||||
*
|
||||
@@ -731,7 +716,6 @@ static inline int hrtimer_reprogram(struct hrtimer *timer,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
-static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { }
|
||||
static inline void retrigger_next_event(void *arg) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
@@ -1601,10 +1585,12 @@ int hrtimers_prepare_cpu(unsigned int cpu)
|
||||
timerqueue_init_head(&cpu_base->clock_base[i].active);
|
||||
}
|
||||
|
||||
- cpu_base->active_bases = 0;
|
||||
cpu_base->cpu = cpu;
|
||||
+ cpu_base->active_bases = 0;
|
||||
cpu_base->hres_active = 0;
|
||||
- hrtimer_init_hres(cpu_base);
|
||||
+ cpu_base->hang_detected = 0;
|
||||
+ cpu_base->next_timer = NULL;
|
||||
+ cpu_base->expires_next = KTIME_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
From 63efcc991a5d6b098605ccdb9a2223f6afbd8602 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:05 +0100
|
||||
Subject: [PATCH 030/450] hrtimer: Make hrtimer_cpu_base.next_timer handling
|
||||
unconditional
|
||||
|
||||
hrtimer_cpu_base.next_timer stores the pointer to the next expiring timer
|
||||
in a cpu base.
|
||||
|
||||
This pointer cannot be dereferenced and is solely used to check whether a
|
||||
hrtimer which is removed is the hrtimer which is the first to expire in the
|
||||
CPU base. If this is the case, then the timer hardware needs to be
|
||||
reprogrammed to avoid an extra interrupt for nothing.
|
||||
|
||||
Again, this is conditional functionality, but there is no compelling reason
|
||||
to make this conditional. As a preparation, hrtimer_cpu_base.next_timer
|
||||
needs to be available unconditonal. Aside of that the upcoming support for
|
||||
softirq based hrtimers requires access to this pointer unconditionally.
|
||||
|
||||
Make the update of hrtimer_cpu_base.next_timer unconditional and remove the
|
||||
ifdef cruft. The impact on CONFIG_HIGH_RES_TIMERS=n && CONFIG_NOHZ=n is
|
||||
marginal as it's just a store on an already dirtied cacheline.
|
||||
|
||||
No functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 4 ++--
|
||||
kernel/time/hrtimer.c | 12 ++----------
|
||||
2 files changed, 4 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index bb7270e8bc37..2d3e1d678a4d 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -164,13 +164,13 @@ enum hrtimer_base_type {
|
||||
* @hres_active: State of high resolution mode
|
||||
* @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hang_detected: The last hrtimer interrupt detected a hang
|
||||
- * @next_timer: Pointer to the first expiring timer
|
||||
* @nr_events: Total number of hrtimer interrupt events
|
||||
* @nr_retries: Total number of hrtimer interrupt retries
|
||||
* @nr_hangs: Total number of hrtimer interrupt hangs
|
||||
* @max_hang_time: Maximum time spent in hrtimer_interrupt
|
||||
* @expires_next: absolute time of the next event, is required for remote
|
||||
* hrtimer enqueue
|
||||
+ * @next_timer: Pointer to the first expiring timer
|
||||
* @clock_base: array of clock bases for this cpu
|
||||
*
|
||||
* Note: next_timer is just an optimization for __remove_hrtimer().
|
||||
@@ -186,13 +186,13 @@ struct hrtimer_cpu_base {
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int in_hrtirq : 1,
|
||||
hang_detected : 1;
|
||||
- struct hrtimer *next_timer;
|
||||
unsigned int nr_events;
|
||||
unsigned short nr_retries;
|
||||
unsigned short nr_hangs;
|
||||
unsigned int max_hang_time;
|
||||
#endif
|
||||
ktime_t expires_next;
|
||||
+ struct hrtimer *next_timer;
|
||||
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
|
||||
} ____cacheline_aligned;
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index b1016aabc73a..e01c2e78c032 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -459,21 +459,13 @@ __next_base(struct hrtimer_cpu_base *cpu_base, unsigned int *active)
|
||||
while ((base = __next_base((cpu_base), &(active))))
|
||||
|
||||
#if defined(CONFIG_NO_HZ_COMMON) || defined(CONFIG_HIGH_RES_TIMERS)
|
||||
-static inline void hrtimer_update_next_timer(struct hrtimer_cpu_base *cpu_base,
|
||||
- struct hrtimer *timer)
|
||||
-{
|
||||
-#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
- cpu_base->next_timer = timer;
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
{
|
||||
struct hrtimer_clock_base *base;
|
||||
unsigned int active = cpu_base->active_bases;
|
||||
ktime_t expires, expires_next = KTIME_MAX;
|
||||
|
||||
- hrtimer_update_next_timer(cpu_base, NULL);
|
||||
+ cpu_base->next_timer = NULL;
|
||||
for_each_active_base(base, cpu_base, active) {
|
||||
struct timerqueue_node *next;
|
||||
struct hrtimer *timer;
|
||||
@@ -483,7 +475,7 @@ static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
if (expires < expires_next) {
|
||||
expires_next = expires;
|
||||
- hrtimer_update_next_timer(cpu_base, timer);
|
||||
+ cpu_base->next_timer = timer;
|
||||
}
|
||||
}
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
From 259346e29614e27a84c4e3cbcbe6a26e61ad56eb Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:06 +0100
|
||||
Subject: [PATCH 031/450] hrtimer: Make hrtimer_reprogramm() unconditional
|
||||
|
||||
hrtimer_reprogram() needs to be available unconditionally for softirq based
|
||||
hrtimers. Move the function and all required struct members out of the
|
||||
CONFIG_HIGH_RES_TIMERS #ifdef.
|
||||
|
||||
There is no functional change because hrtimer_reprogram() is only invoked
|
||||
when hrtimer_cpu_base.hres_active is true. Making it unconditional
|
||||
increases the text size for the CONFIG_HIGH_RES_TIMERS=n case, but avoids
|
||||
replication of that code for the upcoming softirq based hrtimers support.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 6 +-
|
||||
kernel/time/hrtimer.c | 129 +++++++++++++++++++---------------------
|
||||
2 files changed, 65 insertions(+), 70 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 2d3e1d678a4d..98ed35767ac5 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -182,10 +182,10 @@ struct hrtimer_cpu_base {
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set_seq;
|
||||
- unsigned int hres_active : 1;
|
||||
-#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
- unsigned int in_hrtirq : 1,
|
||||
+ unsigned int hres_active : 1,
|
||||
+ in_hrtirq : 1,
|
||||
hang_detected : 1;
|
||||
+#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int nr_events;
|
||||
unsigned short nr_retries;
|
||||
unsigned short nr_hangs;
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index e01c2e78c032..37085a13f19a 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -581,68 +581,6 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
tick_program_event(cpu_base->expires_next, 1);
|
||||
}
|
||||
|
||||
-/*
|
||||
- * When a timer is enqueued and expires earlier than the already enqueued
|
||||
- * timers, we have to check, whether it expires earlier than the timer for
|
||||
- * which the clock event device was armed.
|
||||
- *
|
||||
- * Called with interrupts disabled and base->cpu_base.lock held
|
||||
- */
|
||||
-static void hrtimer_reprogram(struct hrtimer *timer,
|
||||
- struct hrtimer_clock_base *base)
|
||||
-{
|
||||
- struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
- ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
-
|
||||
- WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
|
||||
-
|
||||
- /*
|
||||
- * If the timer is not on the current cpu, we cannot reprogram
|
||||
- * the other cpus clock event device.
|
||||
- */
|
||||
- if (base->cpu_base != cpu_base)
|
||||
- return;
|
||||
-
|
||||
- /*
|
||||
- * If the hrtimer interrupt is running, then it will
|
||||
- * reevaluate the clock bases and reprogram the clock event
|
||||
- * device. The callbacks are always executed in hard interrupt
|
||||
- * context so we don't need an extra check for a running
|
||||
- * callback.
|
||||
- */
|
||||
- if (cpu_base->in_hrtirq)
|
||||
- return;
|
||||
-
|
||||
- /*
|
||||
- * CLOCK_REALTIME timer might be requested with an absolute
|
||||
- * expiry time which is less than base->offset. Set it to 0.
|
||||
- */
|
||||
- if (expires < 0)
|
||||
- expires = 0;
|
||||
-
|
||||
- if (expires >= cpu_base->expires_next)
|
||||
- return;
|
||||
-
|
||||
- /* Update the pointer to the next expiring timer */
|
||||
- cpu_base->next_timer = timer;
|
||||
-
|
||||
- /*
|
||||
- * If a hang was detected in the last timer interrupt then we
|
||||
- * do not schedule a timer which is earlier than the expiry
|
||||
- * which we enforced in the hang detection. We want the system
|
||||
- * to make progress.
|
||||
- */
|
||||
- if (cpu_base->hang_detected)
|
||||
- return;
|
||||
-
|
||||
- /*
|
||||
- * Program the timer hardware. We enforce the expiry for
|
||||
- * events which are already in the past.
|
||||
- */
|
||||
- cpu_base->expires_next = expires;
|
||||
- tick_program_event(expires, 1);
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Retrigger next event is called after clock was set
|
||||
*
|
||||
@@ -703,15 +641,72 @@ static inline int hrtimer_is_hres_enabled(void) { return 0; }
|
||||
static inline void hrtimer_switch_to_hres(void) { }
|
||||
static inline void
|
||||
hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { }
|
||||
-static inline int hrtimer_reprogram(struct hrtimer *timer,
|
||||
- struct hrtimer_clock_base *base)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
static inline void retrigger_next_event(void *arg) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
|
||||
+/*
|
||||
+ * When a timer is enqueued and expires earlier than the already enqueued
|
||||
+ * timers, we have to check, whether it expires earlier than the timer for
|
||||
+ * which the clock event device was armed.
|
||||
+ *
|
||||
+ * Called with interrupts disabled and base->cpu_base.lock held
|
||||
+ */
|
||||
+static void hrtimer_reprogram(struct hrtimer *timer,
|
||||
+ struct hrtimer_clock_base *base)
|
||||
+{
|
||||
+ struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
+ ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
+
|
||||
+ WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
|
||||
+
|
||||
+ /*
|
||||
+ * If the timer is not on the current cpu, we cannot reprogram
|
||||
+ * the other cpus clock event device.
|
||||
+ */
|
||||
+ if (base->cpu_base != cpu_base)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * If the hrtimer interrupt is running, then it will
|
||||
+ * reevaluate the clock bases and reprogram the clock event
|
||||
+ * device. The callbacks are always executed in hard interrupt
|
||||
+ * context so we don't need an extra check for a running
|
||||
+ * callback.
|
||||
+ */
|
||||
+ if (cpu_base->in_hrtirq)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * CLOCK_REALTIME timer might be requested with an absolute
|
||||
+ * expiry time which is less than base->offset. Set it to 0.
|
||||
+ */
|
||||
+ if (expires < 0)
|
||||
+ expires = 0;
|
||||
+
|
||||
+ if (expires >= cpu_base->expires_next)
|
||||
+ return;
|
||||
+
|
||||
+ /* Update the pointer to the next expiring timer */
|
||||
+ cpu_base->next_timer = timer;
|
||||
+
|
||||
+ /*
|
||||
+ * If a hang was detected in the last timer interrupt then we
|
||||
+ * do not schedule a timer which is earlier than the expiry
|
||||
+ * which we enforced in the hang detection. We want the system
|
||||
+ * to make progress.
|
||||
+ */
|
||||
+ if (cpu_base->hang_detected)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * Program the timer hardware. We enforce the expiry for
|
||||
+ * events which are already in the past.
|
||||
+ */
|
||||
+ cpu_base->expires_next = expires;
|
||||
+ tick_program_event(expires, 1);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Clock realtime was set
|
||||
*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
From 24cd3845de20fd8fe195d0e9cc51e64669d39f1f Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:07 +0100
|
||||
Subject: [PATCH 032/450] hrtimer: Make hrtimer_force_reprogramm()
|
||||
unconditionally available
|
||||
|
||||
hrtimer_force_reprogram() needs to be available unconditionally for softirq
|
||||
based hrtimers. Move the function and all required struct members out of
|
||||
the CONFIG_HIGH_RES_TIMERS #ifdef.
|
||||
|
||||
There is no functional change because hrtimer_force_reprogram() is only
|
||||
invoked when hrtimer_cpu_base.hres_active is true and
|
||||
CONFIG_HIGH_RES_TIMERS=y.
|
||||
|
||||
Making it unconditional increases the text size for the
|
||||
CONFIG_HIGH_RES_TIMERS=n case slightly, but avoids replication of that code
|
||||
for the upcoming softirq based hrtimers support. Most of the code gets
|
||||
eliminated in the CONFIG_HIGH_RES_TIMERS=n case by the compiler.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 58 +++++++++++++++++++++----------------------
|
||||
1 file changed, 28 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 37085a13f19a..5fd669dd46be 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -513,34 +513,6 @@ static inline int hrtimer_hres_active(void)
|
||||
return __hrtimer_hres_active(this_cpu_ptr(&hrtimer_bases));
|
||||
}
|
||||
|
||||
-/* High resolution timer related functions */
|
||||
-#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
-
|
||||
-/*
|
||||
- * High resolution timer enabled ?
|
||||
- */
|
||||
-static bool hrtimer_hres_enabled __read_mostly = true;
|
||||
-unsigned int hrtimer_resolution __read_mostly = LOW_RES_NSEC;
|
||||
-EXPORT_SYMBOL_GPL(hrtimer_resolution);
|
||||
-
|
||||
-/*
|
||||
- * Enable / Disable high resolution mode
|
||||
- */
|
||||
-static int __init setup_hrtimer_hres(char *str)
|
||||
-{
|
||||
- return (kstrtobool(str, &hrtimer_hres_enabled) == 0);
|
||||
-}
|
||||
-
|
||||
-__setup("highres=", setup_hrtimer_hres);
|
||||
-
|
||||
-/*
|
||||
- * hrtimer_high_res_enabled - query, if the highres mode is enabled
|
||||
- */
|
||||
-static inline int hrtimer_is_hres_enabled(void)
|
||||
-{
|
||||
- return hrtimer_hres_enabled;
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Reprogram the event source with checking both queues for the
|
||||
* next event
|
||||
@@ -581,6 +553,34 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
tick_program_event(cpu_base->expires_next, 1);
|
||||
}
|
||||
|
||||
+/* High resolution timer related functions */
|
||||
+#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
+
|
||||
+/*
|
||||
+ * High resolution timer enabled ?
|
||||
+ */
|
||||
+static bool hrtimer_hres_enabled __read_mostly = true;
|
||||
+unsigned int hrtimer_resolution __read_mostly = LOW_RES_NSEC;
|
||||
+EXPORT_SYMBOL_GPL(hrtimer_resolution);
|
||||
+
|
||||
+/*
|
||||
+ * Enable / Disable high resolution mode
|
||||
+ */
|
||||
+static int __init setup_hrtimer_hres(char *str)
|
||||
+{
|
||||
+ return (kstrtobool(str, &hrtimer_hres_enabled) == 0);
|
||||
+}
|
||||
+
|
||||
+__setup("highres=", setup_hrtimer_hres);
|
||||
+
|
||||
+/*
|
||||
+ * hrtimer_high_res_enabled - query, if the highres mode is enabled
|
||||
+ */
|
||||
+static inline int hrtimer_is_hres_enabled(void)
|
||||
+{
|
||||
+ return hrtimer_hres_enabled;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Retrigger next event is called after clock was set
|
||||
*
|
||||
@@ -639,8 +639,6 @@ void clock_was_set_delayed(void)
|
||||
|
||||
static inline int hrtimer_is_hres_enabled(void) { return 0; }
|
||||
static inline void hrtimer_switch_to_hres(void) { }
|
||||
-static inline void
|
||||
-hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { }
|
||||
static inline void retrigger_next_event(void *arg) { }
|
||||
|
||||
#endif /* CONFIG_HIGH_RES_TIMERS */
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
From e74c0793383b70b53c785a36f9dcdc7c8d3f6940 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:08 +0100
|
||||
Subject: [PATCH 033/450] hrtimer: Unify handling of hrtimer remove
|
||||
|
||||
When the first hrtimer on the current CPU is removed,
|
||||
hrtimer_force_reprogram() is invoked but only when
|
||||
CONFIG_HIGH_RES_TIMERS=y and hrtimer_cpu_base.hres_active is set.
|
||||
|
||||
hrtimer_force_reprogram() updates hrtimer_cpu_base.expires_next and
|
||||
reprograms the clock event device. When CONFIG_HIGH_RES_TIMERS=y and
|
||||
hrtimer_cpu_base.hres_active is set, a pointless hrtimer interrupt can be
|
||||
prevented.
|
||||
|
||||
hrtimer_check_target() makes the 'can remote enqueue' decision. As soon as
|
||||
hrtimer_check_target() is unconditionally available and
|
||||
hrtimer_cpu_base.expires_next is updated by hrtimer_reprogram(),
|
||||
hrtimer_force_reprogram() needs to be available unconditionally as well to
|
||||
prevent the following scenario with CONFIG_HIGH_RES_TIMERS=n:
|
||||
|
||||
- the first hrtimer on this CPU is removed and hrtimer_force_reprogram() is
|
||||
not executed
|
||||
|
||||
- CPU goes idle (next timer is calculated and hrtimers are taken into
|
||||
account)
|
||||
|
||||
- a hrtimer is enqueued remote on the idle CPU: hrtimer_check_target()
|
||||
compares expiry value and hrtimer_cpu_base.expires_next. The expiry value
|
||||
is after expires_next, so the hrtimer is enqueued. This timer will fire
|
||||
late, if it expires before the effective first hrtimer on this CPU and
|
||||
the comparison was with an outdated expires_next value.
|
||||
|
||||
To prevent this scenario, make hrtimer_force_reprogram() unconditional
|
||||
except the effective reprogramming part, which gets eliminated by the
|
||||
compiler in the CONFIG_HIGH_RES_TIMERS=n case.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 10 ++++------
|
||||
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 5fd669dd46be..ce9a3ef7a796 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -523,9 +523,6 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
{
|
||||
ktime_t expires_next;
|
||||
|
||||
- if (!__hrtimer_hres_active(cpu_base))
|
||||
- return;
|
||||
-
|
||||
expires_next = __hrtimer_get_next_event(cpu_base);
|
||||
|
||||
if (skip_equal && expires_next == cpu_base->expires_next)
|
||||
@@ -534,6 +531,9 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
cpu_base->expires_next = expires_next;
|
||||
|
||||
/*
|
||||
+ * If hres is not active, hardware does not have to be
|
||||
+ * reprogrammed yet.
|
||||
+ *
|
||||
* If a hang was detected in the last timer interrupt then we
|
||||
* leave the hang delay active in the hardware. We want the
|
||||
* system to make progress. That also prevents the following
|
||||
@@ -547,7 +547,7 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
* set. So we'd effectivly block all timers until the T2 event
|
||||
* fires.
|
||||
*/
|
||||
- if (cpu_base->hang_detected)
|
||||
+ if (!__hrtimer_hres_active(cpu_base) || cpu_base->hang_detected)
|
||||
return;
|
||||
|
||||
tick_program_event(cpu_base->expires_next, 1);
|
||||
@@ -848,7 +848,6 @@ static void __remove_hrtimer(struct hrtimer *timer,
|
||||
if (!timerqueue_del(&base->active, &timer->node))
|
||||
cpu_base->active_bases &= ~(1 << base->index);
|
||||
|
||||
-#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
/*
|
||||
* Note: If reprogram is false we do not update
|
||||
* cpu_base->next_timer. This happens when we remove the first
|
||||
@@ -859,7 +858,6 @@ static void __remove_hrtimer(struct hrtimer *timer,
|
||||
*/
|
||||
if (reprogram && timer == cpu_base->next_timer)
|
||||
hrtimer_force_reprogram(cpu_base, 1);
|
||||
-#endif
|
||||
}
|
||||
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
From 8fb098ddff5d47a14a9a5843032b8e206c83ff7a Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:09 +0100
|
||||
Subject: [PATCH 034/450] hrtimer: Unify handling of remote enqueue
|
||||
|
||||
hrtimer_reprogram() is conditionally invoked from hrtimer_start_range_ns()
|
||||
when hrtimer_cpu_base.hres_active is true.
|
||||
|
||||
In the !hres_active case there is a special condition for the nohz_active
|
||||
case:
|
||||
|
||||
If the newly enqueued timer expires before the first expiring timer on a
|
||||
remote CPU then the remote CPU needs to be notified and woken up from a
|
||||
NOHZ idle sleep to take the new first expiring timer into account.
|
||||
|
||||
Previous changes have already established the prerequisites to make the
|
||||
remote enqueue behaviour the same whether high resolution mode is active or
|
||||
not:
|
||||
|
||||
If the to be enqueued timer expires before the first expiring timer on a
|
||||
remote CPU, then it cannot be enqueued there.
|
||||
|
||||
This was done for the high resolution mode because there is no way to
|
||||
access the remote CPU timer hardware. The same is true for NOHZ, but was
|
||||
handled differently by unconditionally enqueuing the timer and waking up
|
||||
the remote CPU so it can reprogram its timer. Again there is no compelling
|
||||
reason for this difference.
|
||||
|
||||
hrtimer_check_target(), which makes the 'can remote enqueue' decision is
|
||||
already unconditional, but not yet functional because nothing updates
|
||||
hrtimer_cpu_base.expires_next in the !hres_active case.
|
||||
|
||||
To unify this the following changes are required:
|
||||
|
||||
1) Make the store of the new first expiry time unconditonal in
|
||||
hrtimer_reprogram() and check __hrtimer_hres_active() before proceeding
|
||||
to the actual hardware access. This check also lets the compiler
|
||||
eliminate the rest of the function in case of CONFIG_HIGH_RES_TIMERS=n.
|
||||
|
||||
2) Invoke hrtimer_reprogram() unconditionally from
|
||||
hrtimer_start_range_ns()
|
||||
|
||||
3) Remove the remote wakeup special case for the !high_res && nohz_active
|
||||
case.
|
||||
|
||||
Confine the timers_nohz_active static key to timer.c which is the only user
|
||||
now.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 18 ++++++------------
|
||||
kernel/time/tick-internal.h | 6 ------
|
||||
kernel/time/timer.c | 9 ++++++++-
|
||||
3 files changed, 14 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index ce9a3ef7a796..35d7d0c8c3d6 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -687,21 +687,24 @@ static void hrtimer_reprogram(struct hrtimer *timer,
|
||||
|
||||
/* Update the pointer to the next expiring timer */
|
||||
cpu_base->next_timer = timer;
|
||||
+ cpu_base->expires_next = expires;
|
||||
|
||||
/*
|
||||
+ * If hres is not active, hardware does not have to be
|
||||
+ * programmed yet.
|
||||
+ *
|
||||
* If a hang was detected in the last timer interrupt then we
|
||||
* do not schedule a timer which is earlier than the expiry
|
||||
* which we enforced in the hang detection. We want the system
|
||||
* to make progress.
|
||||
*/
|
||||
- if (cpu_base->hang_detected)
|
||||
+ if (!__hrtimer_hres_active(cpu_base) || cpu_base->hang_detected)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Program the timer hardware. We enforce the expiry for
|
||||
* events which are already in the past.
|
||||
*/
|
||||
- cpu_base->expires_next = expires;
|
||||
tick_program_event(expires, 1);
|
||||
}
|
||||
|
||||
@@ -940,16 +943,7 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
if (!leftmost)
|
||||
goto unlock;
|
||||
|
||||
- if (!hrtimer_is_hres_active(timer)) {
|
||||
- /*
|
||||
- * Kick to reschedule the next tick to handle the new timer
|
||||
- * on dynticks target.
|
||||
- */
|
||||
- if (is_timers_nohz_active())
|
||||
- wake_up_nohz_cpu(new_base->cpu_base->cpu);
|
||||
- } else {
|
||||
- hrtimer_reprogram(timer, new_base);
|
||||
- }
|
||||
+ hrtimer_reprogram(timer, new_base);
|
||||
unlock:
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
}
|
||||
diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h
|
||||
index 4ac74dff59f0..e277284c2831 100644
|
||||
--- a/kernel/time/tick-internal.h
|
||||
+++ b/kernel/time/tick-internal.h
|
||||
@@ -151,18 +151,12 @@ static inline void tick_nohz_init(void) { }
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
extern unsigned long tick_nohz_active;
|
||||
extern void timers_update_nohz(void);
|
||||
-extern struct static_key_false timers_nohz_active;
|
||||
-static inline bool is_timers_nohz_active(void)
|
||||
-{
|
||||
- return static_branch_unlikely(&timers_nohz_active);
|
||||
-}
|
||||
# ifdef CONFIG_SMP
|
||||
extern struct static_key_false timers_migration_enabled;
|
||||
# endif
|
||||
#else /* CONFIG_NO_HZ_COMMON */
|
||||
static inline void timers_update_nohz(void) { }
|
||||
#define tick_nohz_active (0)
|
||||
-static inline bool is_timers_nohz_active(void) { return false; }
|
||||
#endif
|
||||
|
||||
DECLARE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases);
|
||||
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
|
||||
index 9b644e70b660..3b6c7e287f23 100644
|
||||
--- a/kernel/time/timer.c
|
||||
+++ b/kernel/time/timer.c
|
||||
@@ -210,7 +210,7 @@ static DEFINE_PER_CPU(struct timer_base, timer_bases[NR_BASES]);
|
||||
|
||||
#ifdef CONFIG_NO_HZ_COMMON
|
||||
|
||||
-DEFINE_STATIC_KEY_FALSE(timers_nohz_active);
|
||||
+static DEFINE_STATIC_KEY_FALSE(timers_nohz_active);
|
||||
static DEFINE_MUTEX(timer_keys_mutex);
|
||||
|
||||
static void timer_update_keys(struct work_struct *work);
|
||||
@@ -260,6 +260,13 @@ int timer_migration_handler(struct ctl_table *table, int write,
|
||||
mutex_unlock(&timer_keys_mutex);
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+static inline bool is_timers_nohz_active(void)
|
||||
+{
|
||||
+ return static_branch_unlikely(&timers_nohz_active);
|
||||
+}
|
||||
+#else
|
||||
+static inline bool is_timers_nohz_active(void) { return false; }
|
||||
#endif /* NO_HZ_COMMON */
|
||||
|
||||
static unsigned long round_jiffies_common(unsigned long j, int cpu,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
From 13b3625a91c6df2a63dfc4ac26db33008d63a7e2 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:10 +0100
|
||||
Subject: [PATCH 035/450] hrtimer: Make remote enqueue decision less
|
||||
restrictive
|
||||
|
||||
The current decision whether a timer can be queued on a remote CPU checks
|
||||
for timer->expiry <= remote_cpu_base.expires_next.
|
||||
|
||||
This is too restrictive because a timer with the same expiry time as an
|
||||
existing timer will be enqueued on right-hand size of the existing timer
|
||||
inside the rbtree, i.e. behind the first expiring timer.
|
||||
|
||||
So its safe to allow enqueuing timers with the same expiry time as the
|
||||
first expiring timer on a remote CPU base.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 35d7d0c8c3d6..1b2866645c83 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -168,7 +168,7 @@ hrtimer_check_target(struct hrtimer *timer, struct hrtimer_clock_base *new_base)
|
||||
ktime_t expires;
|
||||
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), new_base->offset);
|
||||
- return expires <= new_base->cpu_base->expires_next;
|
||||
+ return expires < new_base->cpu_base->expires_next;
|
||||
}
|
||||
|
||||
static inline
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
From 0cf40556907398391a02e49acbb93e2be5f0be98 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:11 +0100
|
||||
Subject: [PATCH 036/450] hrtimer: Remove base argument from
|
||||
hrtimer_reprogram()
|
||||
|
||||
hrtimer_reprogram() must have access to the hrtimer_clock_base of the new
|
||||
first expiring timer to access hrtimer_clock_base.offset for adjusting the
|
||||
expiry time to CLOCK_MONOTONIC. This is required to evaluate whether the
|
||||
new left most timer in the hrtimer_clock_base is the first expiring timer
|
||||
of all clock bases in a hrtimer_cpu_base.
|
||||
|
||||
The only user of hrtimer_reprogram() is hrtimer_start_range_ns(), which has
|
||||
a pointer to hrtimer_clock_base already and hands it in as an argument. But
|
||||
hrtimer_start_range_ns() will be split for the upcoming support for softirq
|
||||
based hrtimers to avoid code duplication and will lose the direct access to
|
||||
the clock base pointer.
|
||||
|
||||
Instead of handing in timer and timer->base as an argument remove the base
|
||||
argument from hrtimer_reprogram() and retrieve the clock base internally.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 1b2866645c83..9030dcb3ef14 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -650,10 +650,10 @@ static inline void retrigger_next_event(void *arg) { }
|
||||
*
|
||||
* Called with interrupts disabled and base->cpu_base.lock held
|
||||
*/
|
||||
-static void hrtimer_reprogram(struct hrtimer *timer,
|
||||
- struct hrtimer_clock_base *base)
|
||||
+static void hrtimer_reprogram(struct hrtimer *timer)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
+ struct hrtimer_clock_base *base = timer->base;
|
||||
ktime_t expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
|
||||
WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
|
||||
@@ -943,7 +943,7 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
if (!leftmost)
|
||||
goto unlock;
|
||||
|
||||
- hrtimer_reprogram(timer, new_base);
|
||||
+ hrtimer_reprogram(timer);
|
||||
unlock:
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
From e884985cb8d91c0547a8d1cdda126bf7e0019c1c Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:12 +0100
|
||||
Subject: [PATCH 037/450] hrtimer: Split hrtimer_start_range_ns()
|
||||
|
||||
Preparatory patch for softirq based hrtimers to avoid code duplication. No
|
||||
functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 44 +++++++++++++++++++++++--------------------
|
||||
1 file changed, 24 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 9030dcb3ef14..687a8d903a18 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -909,22 +909,11 @@ static inline ktime_t hrtimer_update_lowres(struct hrtimer *timer, ktime_t tim,
|
||||
return tim;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * hrtimer_start_range_ns - (re)start an hrtimer
|
||||
- * @timer: the timer to be added
|
||||
- * @tim: expiry time
|
||||
- * @delta_ns: "slack" range for the timer
|
||||
- * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
- */
|
||||
-void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
- u64 delta_ns, const enum hrtimer_mode mode)
|
||||
+static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
+ u64 delta_ns, const enum hrtimer_mode mode,
|
||||
+ struct hrtimer_clock_base *base)
|
||||
{
|
||||
- struct hrtimer_clock_base *base, *new_base;
|
||||
- unsigned long flags;
|
||||
- int leftmost;
|
||||
-
|
||||
- base = lock_hrtimer_base(timer, &flags);
|
||||
+ struct hrtimer_clock_base *new_base;
|
||||
|
||||
/* Remove an active timer from the queue: */
|
||||
remove_hrtimer(timer, base, true);
|
||||
@@ -939,12 +928,27 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
/* Switch the timer base, if necessary: */
|
||||
new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
|
||||
|
||||
- leftmost = enqueue_hrtimer(timer, new_base, mode);
|
||||
- if (!leftmost)
|
||||
- goto unlock;
|
||||
+ return enqueue_hrtimer(timer, new_base, mode);
|
||||
+}
|
||||
+/**
|
||||
+ * hrtimer_start_range_ns - (re)start an hrtimer
|
||||
+ * @timer: the timer to be added
|
||||
+ * @tim: expiry time
|
||||
+ * @delta_ns: "slack" range for the timer
|
||||
+ * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
+ * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
+ */
|
||||
+void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
+ u64 delta_ns, const enum hrtimer_mode mode)
|
||||
+{
|
||||
+ struct hrtimer_clock_base *base;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ base = lock_hrtimer_base(timer, &flags);
|
||||
+
|
||||
+ if (__hrtimer_start_range_ns(timer, tim, delta_ns, mode, base))
|
||||
+ hrtimer_reprogram(timer);
|
||||
|
||||
- hrtimer_reprogram(timer);
|
||||
-unlock:
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_start_range_ns);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
From 4317f172bc6903167e82cc24d11bea7e21148440 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:13 +0100
|
||||
Subject: [PATCH 038/450] hrtimer: Split __hrtimer_get_next_event()
|
||||
|
||||
Preparatory patch for softirq based hrtimers to avoid code duplication. No
|
||||
functional change.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 20 ++++++++++++++++----
|
||||
1 file changed, 16 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 687a8d903a18..2382bc5d8e4d 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -459,13 +459,13 @@ __next_base(struct hrtimer_cpu_base *cpu_base, unsigned int *active)
|
||||
while ((base = __next_base((cpu_base), &(active))))
|
||||
|
||||
#if defined(CONFIG_NO_HZ_COMMON) || defined(CONFIG_HIGH_RES_TIMERS)
|
||||
-static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
+static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
|
||||
+ unsigned int active,
|
||||
+ ktime_t expires_next)
|
||||
{
|
||||
struct hrtimer_clock_base *base;
|
||||
- unsigned int active = cpu_base->active_bases;
|
||||
- ktime_t expires, expires_next = KTIME_MAX;
|
||||
+ ktime_t expires;
|
||||
|
||||
- cpu_base->next_timer = NULL;
|
||||
for_each_active_base(base, cpu_base, active) {
|
||||
struct timerqueue_node *next;
|
||||
struct hrtimer *timer;
|
||||
@@ -487,6 +487,18 @@ static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
expires_next = 0;
|
||||
return expires_next;
|
||||
}
|
||||
+
|
||||
+static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
+{
|
||||
+ unsigned int active = cpu_base->active_bases;
|
||||
+ ktime_t expires_next = KTIME_MAX;
|
||||
+
|
||||
+ cpu_base->next_timer = NULL;
|
||||
+
|
||||
+ expires_next = __hrtimer_next_event_base(cpu_base, active, expires_next);
|
||||
+
|
||||
+ return expires_next;
|
||||
+}
|
||||
#endif
|
||||
|
||||
static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
From b7ccaa85f3ae93369f44be697f5db72c1c3f5c10 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:14 +0100
|
||||
Subject: [PATCH 039/450] hrtimer: Use irqsave/irqrestore around
|
||||
__run_hrtimer()
|
||||
|
||||
__run_hrtimer() is called with the hrtimer_cpu_base.lock held and
|
||||
interrupts disabled. Before invoking the timer callback the base lock is
|
||||
dropped, but interrupts stay disabled.
|
||||
|
||||
The upcoming support for softirq based hrtimers requires that interrupts
|
||||
are enabled before the timer callback is invoked.
|
||||
|
||||
To avoid code duplication, take hrtimer_cpu_base.lock with
|
||||
raw_spin_lock_irqsave(flags) at the call site and hand in the flags as
|
||||
argument. So raw_spin_unlock_irqrestore() before the callback invocation
|
||||
will either keep interrupts disabled in interrupt context or restore to
|
||||
interrupt enabled state when called from softirq context.
|
||||
|
||||
Suggested-by: Peter Zijlstra <peterz@infradead.org>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 31 ++++++++++++++++++-------------
|
||||
1 file changed, 18 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 2382bc5d8e4d..86cdc9a76911 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -1163,7 +1163,8 @@ EXPORT_SYMBOL_GPL(hrtimer_active);
|
||||
|
||||
static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
struct hrtimer_clock_base *base,
|
||||
- struct hrtimer *timer, ktime_t *now)
|
||||
+ struct hrtimer *timer, ktime_t *now,
|
||||
+ unsigned long flags)
|
||||
{
|
||||
enum hrtimer_restart (*fn)(struct hrtimer *);
|
||||
int restart;
|
||||
@@ -1198,11 +1199,11 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
* protected against migration to a different CPU even if the lock
|
||||
* is dropped.
|
||||
*/
|
||||
- raw_spin_unlock(&cpu_base->lock);
|
||||
+ raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
trace_hrtimer_expire_entry(timer, now);
|
||||
restart = fn(timer);
|
||||
trace_hrtimer_expire_exit(timer);
|
||||
- raw_spin_lock(&cpu_base->lock);
|
||||
+ raw_spin_lock_irq(&cpu_base->lock);
|
||||
|
||||
/*
|
||||
* Note: We clear the running state after enqueue_hrtimer and
|
||||
@@ -1230,7 +1231,8 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
base->running = NULL;
|
||||
}
|
||||
|
||||
-static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
|
||||
+static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
|
||||
+ unsigned long flags)
|
||||
{
|
||||
struct hrtimer_clock_base *base;
|
||||
unsigned int active = cpu_base->active_bases;
|
||||
@@ -1261,7 +1263,7 @@ static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
|
||||
if (basenow < hrtimer_get_softexpires_tv64(timer))
|
||||
break;
|
||||
|
||||
- __run_hrtimer(cpu_base, base, timer, &basenow);
|
||||
+ __run_hrtimer(cpu_base, base, timer, &basenow, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1276,13 +1278,14 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
ktime_t expires_next, now, entry_time, delta;
|
||||
+ unsigned long flags;
|
||||
int retries = 0;
|
||||
|
||||
BUG_ON(!cpu_base->hres_active);
|
||||
cpu_base->nr_events++;
|
||||
dev->next_event = KTIME_MAX;
|
||||
|
||||
- raw_spin_lock(&cpu_base->lock);
|
||||
+ raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
entry_time = now = hrtimer_update_base(cpu_base);
|
||||
retry:
|
||||
cpu_base->in_hrtirq = 1;
|
||||
@@ -1295,7 +1298,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
*/
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
|
||||
- __hrtimer_run_queues(cpu_base, now);
|
||||
+ __hrtimer_run_queues(cpu_base, now, flags);
|
||||
|
||||
/* Reevaluate the clock bases for the next expiry */
|
||||
expires_next = __hrtimer_get_next_event(cpu_base);
|
||||
@@ -1305,7 +1308,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
*/
|
||||
cpu_base->expires_next = expires_next;
|
||||
cpu_base->in_hrtirq = 0;
|
||||
- raw_spin_unlock(&cpu_base->lock);
|
||||
+ raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
|
||||
/* Reprogramming necessary ? */
|
||||
if (!tick_program_event(expires_next, 0)) {
|
||||
@@ -1326,7 +1329,7 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
* Acquire base lock for updating the offsets and retrieving
|
||||
* the current time.
|
||||
*/
|
||||
- raw_spin_lock(&cpu_base->lock);
|
||||
+ raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
cpu_base->nr_retries++;
|
||||
if (++retries < 3)
|
||||
@@ -1339,7 +1342,8 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
*/
|
||||
cpu_base->nr_hangs++;
|
||||
cpu_base->hang_detected = 1;
|
||||
- raw_spin_unlock(&cpu_base->lock);
|
||||
+ raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
+
|
||||
delta = ktime_sub(now, entry_time);
|
||||
if ((unsigned int)delta > cpu_base->max_hang_time)
|
||||
cpu_base->max_hang_time = (unsigned int) delta;
|
||||
@@ -1381,6 +1385,7 @@ static inline void __hrtimer_peek_ahead_timers(void) { }
|
||||
void hrtimer_run_queues(void)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
+ unsigned long flags;
|
||||
ktime_t now;
|
||||
|
||||
if (__hrtimer_hres_active(cpu_base))
|
||||
@@ -1398,10 +1403,10 @@ void hrtimer_run_queues(void)
|
||||
return;
|
||||
}
|
||||
|
||||
- raw_spin_lock(&cpu_base->lock);
|
||||
+ raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
- __hrtimer_run_queues(cpu_base, now);
|
||||
- raw_spin_unlock(&cpu_base->lock);
|
||||
+ __hrtimer_run_queues(cpu_base, now, flags);
|
||||
+ raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
From ca683556d23c0f7dd07d63f56d9becd628db4386 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:15 +0100
|
||||
Subject: [PATCH 040/450] hrtimer: Add clock bases and hrtimer mode for soft
|
||||
irq context
|
||||
|
||||
hrtimer callback functions are always executed in hard interrupt
|
||||
context. Users of hrtimer which need their timer function to be executed
|
||||
in soft interrupt context, make use of tasklets to get the proper context.
|
||||
|
||||
Add additional hrtimer clock bases for timers which must expire in softirq
|
||||
context, so the detour via the tasklet can be avoided. This is also
|
||||
required for RT, where the majority of hrtimer is moved into softirq
|
||||
hrtimer context.
|
||||
|
||||
The selection of the expiry mode happens via a mode bit. Introduce
|
||||
HRTIMER_MODE_SOFT and the matching combinations with the ABS/REL/PINNED
|
||||
bits and update the decoding of hrtimer_mode in tracepoints.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 14 ++++++++++++++
|
||||
include/trace/events/timer.h | 6 +++++-
|
||||
kernel/time/hrtimer.c | 20 ++++++++++++++++++++
|
||||
3 files changed, 39 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 98ed35767ac5..26ae8a868ea8 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -33,14 +33,24 @@ struct hrtimer_cpu_base;
|
||||
* HRTIMER_MODE_REL - Time value is relative to now
|
||||
* HRTIMER_MODE_PINNED - Timer is bound to CPU (is only considered
|
||||
* when starting the timer)
|
||||
+ * HRTIMER_MODE_SOFT - Timer callback function will be executed in
|
||||
+ * soft irq context
|
||||
*/
|
||||
enum hrtimer_mode {
|
||||
HRTIMER_MODE_ABS = 0x00,
|
||||
HRTIMER_MODE_REL = 0x01,
|
||||
HRTIMER_MODE_PINNED = 0x02,
|
||||
+ HRTIMER_MODE_SOFT = 0x04,
|
||||
|
||||
HRTIMER_MODE_ABS_PINNED = HRTIMER_MODE_ABS | HRTIMER_MODE_PINNED,
|
||||
HRTIMER_MODE_REL_PINNED = HRTIMER_MODE_REL | HRTIMER_MODE_PINNED,
|
||||
+
|
||||
+ HRTIMER_MODE_ABS_SOFT = HRTIMER_MODE_ABS | HRTIMER_MODE_SOFT,
|
||||
+ HRTIMER_MODE_REL_SOFT = HRTIMER_MODE_REL | HRTIMER_MODE_SOFT,
|
||||
+
|
||||
+ HRTIMER_MODE_ABS_PINNED_SOFT = HRTIMER_MODE_ABS_PINNED | HRTIMER_MODE_SOFT,
|
||||
+ HRTIMER_MODE_REL_PINNED_SOFT = HRTIMER_MODE_REL_PINNED | HRTIMER_MODE_SOFT,
|
||||
+
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -151,6 +161,10 @@ enum hrtimer_base_type {
|
||||
HRTIMER_BASE_REALTIME,
|
||||
HRTIMER_BASE_BOOTTIME,
|
||||
HRTIMER_BASE_TAI,
|
||||
+ HRTIMER_BASE_MONOTONIC_SOFT,
|
||||
+ HRTIMER_BASE_REALTIME_SOFT,
|
||||
+ HRTIMER_BASE_BOOTTIME_SOFT,
|
||||
+ HRTIMER_BASE_TAI_SOFT,
|
||||
HRTIMER_MAX_CLOCK_BASES,
|
||||
};
|
||||
|
||||
diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h
|
||||
index 744b4310b24b..a57e4ee989d6 100644
|
||||
--- a/include/trace/events/timer.h
|
||||
+++ b/include/trace/events/timer.h
|
||||
@@ -148,7 +148,11 @@ DEFINE_EVENT(timer_class, timer_cancel,
|
||||
{ HRTIMER_MODE_ABS, "ABS" }, \
|
||||
{ HRTIMER_MODE_REL, "REL" }, \
|
||||
{ HRTIMER_MODE_ABS_PINNED, "ABS|PINNED" }, \
|
||||
- { HRTIMER_MODE_REL_PINNED, "REL|PINNED" })
|
||||
+ { HRTIMER_MODE_REL_PINNED, "REL|PINNED" }, \
|
||||
+ { HRTIMER_MODE_ABS_SOFT, "ABS|SOFT" }, \
|
||||
+ { HRTIMER_MODE_REL_SOFT, "REL|SOFT" }, \
|
||||
+ { HRTIMER_MODE_ABS_PINNED_SOFT, "ABS|PINNED|SOFT" }, \
|
||||
+ { HRTIMER_MODE_REL_PINNED_SOFT, "REL|PINNED|SOFT" })
|
||||
|
||||
/**
|
||||
* hrtimer_init - called when the hrtimer is initialized
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 86cdc9a76911..b5121845d12c 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -92,6 +92,26 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
|
||||
.clockid = CLOCK_TAI,
|
||||
.get_time = &ktime_get_clocktai,
|
||||
},
|
||||
+ {
|
||||
+ .index = HRTIMER_BASE_MONOTONIC_SOFT,
|
||||
+ .clockid = CLOCK_MONOTONIC,
|
||||
+ .get_time = &ktime_get,
|
||||
+ },
|
||||
+ {
|
||||
+ .index = HRTIMER_BASE_REALTIME_SOFT,
|
||||
+ .clockid = CLOCK_REALTIME,
|
||||
+ .get_time = &ktime_get_real,
|
||||
+ },
|
||||
+ {
|
||||
+ .index = HRTIMER_BASE_BOOTTIME_SOFT,
|
||||
+ .clockid = CLOCK_BOOTTIME,
|
||||
+ .get_time = &ktime_get_boottime,
|
||||
+ },
|
||||
+ {
|
||||
+ .index = HRTIMER_BASE_TAI_SOFT,
|
||||
+ .clockid = CLOCK_TAI,
|
||||
+ .get_time = &ktime_get_clocktai,
|
||||
+ },
|
||||
}
|
||||
};
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
From cae6bc5fa3632fb13d559189b382b8935b165be1 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:16 +0100
|
||||
Subject: [PATCH 041/450] hrtimer: Prepare handling of hard and softirq based
|
||||
hrtimers
|
||||
|
||||
The softirq based hrtimer can utilize most of the existing hrtimers
|
||||
functions, but need to operate on a different data set.
|
||||
|
||||
Add an active_mask argument to various functions so the hard and soft bases
|
||||
can be selected. Fixup the existing callers and hand in the ACTIVE_HARD
|
||||
mask.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 38 +++++++++++++++++++++++++++++---------
|
||||
1 file changed, 29 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index b5121845d12c..9a1f2b00c847 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -59,6 +59,15 @@
|
||||
|
||||
#include "tick-internal.h"
|
||||
|
||||
+/*
|
||||
+ * Masks for selecting the soft and hard context timers from
|
||||
+ * cpu_base->active
|
||||
+ */
|
||||
+#define MASK_SHIFT (HRTIMER_BASE_MONOTONIC_SOFT)
|
||||
+#define HRTIMER_ACTIVE_HARD ((1U << MASK_SHIFT) - 1)
|
||||
+#define HRTIMER_ACTIVE_SOFT (HRTIMER_ACTIVE_HARD << MASK_SHIFT)
|
||||
+#define HRTIMER_ACTIVE_ALL (HRTIMER_ACTIVE_SOFT | HRTIMER_ACTIVE_HARD)
|
||||
+
|
||||
/*
|
||||
* The timer bases:
|
||||
*
|
||||
@@ -508,13 +517,24 @@ static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
|
||||
return expires_next;
|
||||
}
|
||||
|
||||
-static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base)
|
||||
+/*
|
||||
+ * Recomputes cpu_base::*next_timer and returns the earliest expires_next but
|
||||
+ * does not set cpu_base::*expires_next, that is done by hrtimer_reprogram.
|
||||
+ *
|
||||
+ * @active_mask must be one of:
|
||||
+ * - HRTIMER_ACTIVE,
|
||||
+ * - HRTIMER_ACTIVE_SOFT, or
|
||||
+ * - HRTIMER_ACTIVE_HARD.
|
||||
+ */
|
||||
+static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base,
|
||||
+ unsigned int active_mask)
|
||||
{
|
||||
- unsigned int active = cpu_base->active_bases;
|
||||
+ unsigned int active;
|
||||
ktime_t expires_next = KTIME_MAX;
|
||||
|
||||
cpu_base->next_timer = NULL;
|
||||
|
||||
+ active = cpu_base->active_bases & active_mask;
|
||||
expires_next = __hrtimer_next_event_base(cpu_base, active, expires_next);
|
||||
|
||||
return expires_next;
|
||||
@@ -555,7 +575,7 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
{
|
||||
ktime_t expires_next;
|
||||
|
||||
- expires_next = __hrtimer_get_next_event(cpu_base);
|
||||
+ expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
|
||||
if (skip_equal && expires_next == cpu_base->expires_next)
|
||||
return;
|
||||
@@ -1078,7 +1098,7 @@ u64 hrtimer_get_next_event(void)
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
|
||||
if (!__hrtimer_hres_active(cpu_base))
|
||||
- expires = __hrtimer_get_next_event(cpu_base);
|
||||
+ expires = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
|
||||
@@ -1252,10 +1272,10 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
|
||||
}
|
||||
|
||||
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
|
||||
- unsigned long flags)
|
||||
+ unsigned long flags, unsigned int active_mask)
|
||||
{
|
||||
struct hrtimer_clock_base *base;
|
||||
- unsigned int active = cpu_base->active_bases;
|
||||
+ unsigned int active = cpu_base->active_bases & active_mask;
|
||||
|
||||
for_each_active_base(base, cpu_base, active) {
|
||||
struct timerqueue_node *node;
|
||||
@@ -1318,10 +1338,10 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
*/
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
|
||||
- __hrtimer_run_queues(cpu_base, now, flags);
|
||||
+ __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD);
|
||||
|
||||
/* Reevaluate the clock bases for the next expiry */
|
||||
- expires_next = __hrtimer_get_next_event(cpu_base);
|
||||
+ expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
/*
|
||||
* Store the new expiry value so the migration code can verify
|
||||
* against it.
|
||||
@@ -1425,7 +1445,7 @@ void hrtimer_run_queues(void)
|
||||
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
- __hrtimer_run_queues(cpu_base, now, flags);
|
||||
+ __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD);
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,506 +0,0 @@
|
||||
From 478a104cc093e2bd50b75791abc57f3a7957e566 Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:17 +0100
|
||||
Subject: [PATCH 042/450] hrtimer: Implement support for softirq based hrtimers
|
||||
|
||||
hrtimer callbacks are always invoked in hard interrupt context. Several
|
||||
users in tree require soft interrupt context for their callbacks and
|
||||
achieve this by combining a hrtimer with a tasklet. The hrtimer schedules
|
||||
the tasklet in hard interrupt context and the tasklet callback gets invoked
|
||||
in softirq context later.
|
||||
|
||||
That's suboptimal and aside of that the real-time patch moves most of the
|
||||
hrtimers into softirq context. So adding native support for hrtimers
|
||||
expiring in softirq context is a valuable extension for both mainline and
|
||||
the RT patch set.
|
||||
|
||||
Each valid hrtimer clock id has two associated hrtimer clock bases: one for
|
||||
timers expiring in hardirq context and one for timers expiring in softirq
|
||||
context.
|
||||
|
||||
Implement the functionality to associate a hrtimer with the hard or softirq
|
||||
related clock bases and update the relevant functions to take them into
|
||||
account when the next expiry time needs to be evaluated.
|
||||
|
||||
Add a check into the hard interrupt context handler functions to check
|
||||
whether the first expiring softirq based timer has expired. If it's expired
|
||||
the softirq is raised and the accounting of softirq based timers to
|
||||
evaluate the next expiry time for programming the timer hardware is skipped
|
||||
until the softirq processing has finished. At the end of the softirq
|
||||
processing the regular processing is resumed.
|
||||
|
||||
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Suggested-by: Peter Zijlstra <peterz@infradead.org>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/hrtimer.h | 21 ++++-
|
||||
kernel/time/hrtimer.c | 199 ++++++++++++++++++++++++++++++++++------
|
||||
2 files changed, 189 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
|
||||
index 26ae8a868ea8..c7902ca7c9f4 100644
|
||||
--- a/include/linux/hrtimer.h
|
||||
+++ b/include/linux/hrtimer.h
|
||||
@@ -103,6 +103,7 @@ enum hrtimer_restart {
|
||||
* @base: pointer to the timer base (per cpu and per clock)
|
||||
* @state: state information (See bit values above)
|
||||
* @is_rel: Set if the timer was armed relative
|
||||
+ * @is_soft: Set if hrtimer will be expired in soft interrupt context.
|
||||
*
|
||||
* The hrtimer structure must be initialized by hrtimer_init()
|
||||
*/
|
||||
@@ -113,6 +114,7 @@ struct hrtimer {
|
||||
struct hrtimer_clock_base *base;
|
||||
u8 state;
|
||||
u8 is_rel;
|
||||
+ u8 is_soft;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -178,13 +180,18 @@ enum hrtimer_base_type {
|
||||
* @hres_active: State of high resolution mode
|
||||
* @in_hrtirq: hrtimer_interrupt() is currently executing
|
||||
* @hang_detected: The last hrtimer interrupt detected a hang
|
||||
+ * @softirq_activated: displays, if the softirq is raised - update of softirq
|
||||
+ * related settings is not required then.
|
||||
* @nr_events: Total number of hrtimer interrupt events
|
||||
* @nr_retries: Total number of hrtimer interrupt retries
|
||||
* @nr_hangs: Total number of hrtimer interrupt hangs
|
||||
* @max_hang_time: Maximum time spent in hrtimer_interrupt
|
||||
* @expires_next: absolute time of the next event, is required for remote
|
||||
- * hrtimer enqueue
|
||||
+ * hrtimer enqueue; it is the total first expiry time (hard
|
||||
+ * and soft hrtimer are taken into account)
|
||||
* @next_timer: Pointer to the first expiring timer
|
||||
+ * @softirq_expires_next: Time to check, if soft queues needs also to be expired
|
||||
+ * @softirq_next_timer: Pointer to the first expiring softirq based timer
|
||||
* @clock_base: array of clock bases for this cpu
|
||||
*
|
||||
* Note: next_timer is just an optimization for __remove_hrtimer().
|
||||
@@ -196,9 +203,10 @@ struct hrtimer_cpu_base {
|
||||
unsigned int cpu;
|
||||
unsigned int active_bases;
|
||||
unsigned int clock_was_set_seq;
|
||||
- unsigned int hres_active : 1,
|
||||
- in_hrtirq : 1,
|
||||
- hang_detected : 1;
|
||||
+ unsigned int hres_active : 1,
|
||||
+ in_hrtirq : 1,
|
||||
+ hang_detected : 1,
|
||||
+ softirq_activated : 1;
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
unsigned int nr_events;
|
||||
unsigned short nr_retries;
|
||||
@@ -207,6 +215,8 @@ struct hrtimer_cpu_base {
|
||||
#endif
|
||||
ktime_t expires_next;
|
||||
struct hrtimer *next_timer;
|
||||
+ ktime_t softirq_expires_next;
|
||||
+ struct hrtimer *softirq_next_timer;
|
||||
struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES];
|
||||
} ____cacheline_aligned;
|
||||
|
||||
@@ -379,7 +389,8 @@ extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
* @timer: the timer to be added
|
||||
* @tim: expiry time
|
||||
* @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
+ * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);
|
||||
+ * softirq based mode is considered for debug purpose only!
|
||||
*/
|
||||
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,
|
||||
const enum hrtimer_mode mode)
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 9a1f2b00c847..66d7a12d7256 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -411,7 +411,8 @@ static inline void debug_hrtimer_init(struct hrtimer *timer)
|
||||
debug_object_init(timer, &hrtimer_debug_descr);
|
||||
}
|
||||
|
||||
-static inline void debug_hrtimer_activate(struct hrtimer *timer)
|
||||
+static inline void debug_hrtimer_activate(struct hrtimer *timer,
|
||||
+ enum hrtimer_mode mode)
|
||||
{
|
||||
debug_object_activate(timer, &hrtimer_debug_descr);
|
||||
}
|
||||
@@ -444,8 +445,10 @@ void destroy_hrtimer_on_stack(struct hrtimer *timer)
|
||||
EXPORT_SYMBOL_GPL(destroy_hrtimer_on_stack);
|
||||
|
||||
#else
|
||||
+
|
||||
static inline void debug_hrtimer_init(struct hrtimer *timer) { }
|
||||
-static inline void debug_hrtimer_activate(struct hrtimer *timer) { }
|
||||
+static inline void debug_hrtimer_activate(struct hrtimer *timer,
|
||||
+ enum hrtimer_mode mode) { }
|
||||
static inline void debug_hrtimer_deactivate(struct hrtimer *timer) { }
|
||||
#endif
|
||||
|
||||
@@ -460,7 +463,7 @@ debug_init(struct hrtimer *timer, clockid_t clockid,
|
||||
static inline void debug_activate(struct hrtimer *timer,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
- debug_hrtimer_activate(timer);
|
||||
+ debug_hrtimer_activate(timer, mode);
|
||||
trace_hrtimer_start(timer, mode);
|
||||
}
|
||||
|
||||
@@ -487,7 +490,6 @@ __next_base(struct hrtimer_cpu_base *cpu_base, unsigned int *active)
|
||||
#define for_each_active_base(base, cpu_base, active) \
|
||||
while ((base = __next_base((cpu_base), &(active))))
|
||||
|
||||
-#if defined(CONFIG_NO_HZ_COMMON) || defined(CONFIG_HIGH_RES_TIMERS)
|
||||
static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
|
||||
unsigned int active,
|
||||
ktime_t expires_next)
|
||||
@@ -504,7 +506,10 @@ static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
|
||||
expires = ktime_sub(hrtimer_get_expires(timer), base->offset);
|
||||
if (expires < expires_next) {
|
||||
expires_next = expires;
|
||||
- cpu_base->next_timer = timer;
|
||||
+ if (timer->is_soft)
|
||||
+ cpu_base->softirq_next_timer = timer;
|
||||
+ else
|
||||
+ cpu_base->next_timer = timer;
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -521,25 +526,42 @@ static ktime_t __hrtimer_next_event_base(struct hrtimer_cpu_base *cpu_base,
|
||||
* Recomputes cpu_base::*next_timer and returns the earliest expires_next but
|
||||
* does not set cpu_base::*expires_next, that is done by hrtimer_reprogram.
|
||||
*
|
||||
+ * When a softirq is pending, we can ignore the HRTIMER_ACTIVE_SOFT bases,
|
||||
+ * those timers will get run whenever the softirq gets handled, at the end of
|
||||
+ * hrtimer_run_softirq(), hrtimer_update_softirq_timer() will re-add these bases.
|
||||
+ *
|
||||
+ * Therefore softirq values are those from the HRTIMER_ACTIVE_SOFT clock bases.
|
||||
+ * The !softirq values are the minima across HRTIMER_ACTIVE_ALL, unless an actual
|
||||
+ * softirq is pending, in which case they're the minima of HRTIMER_ACTIVE_HARD.
|
||||
+ *
|
||||
* @active_mask must be one of:
|
||||
- * - HRTIMER_ACTIVE,
|
||||
+ * - HRTIMER_ACTIVE_ALL,
|
||||
* - HRTIMER_ACTIVE_SOFT, or
|
||||
* - HRTIMER_ACTIVE_HARD.
|
||||
*/
|
||||
-static ktime_t __hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base,
|
||||
- unsigned int active_mask)
|
||||
+static ktime_t
|
||||
+__hrtimer_get_next_event(struct hrtimer_cpu_base *cpu_base, unsigned int active_mask)
|
||||
{
|
||||
unsigned int active;
|
||||
+ struct hrtimer *next_timer = NULL;
|
||||
ktime_t expires_next = KTIME_MAX;
|
||||
|
||||
- cpu_base->next_timer = NULL;
|
||||
+ if (!cpu_base->softirq_activated && (active_mask & HRTIMER_ACTIVE_SOFT)) {
|
||||
+ active = cpu_base->active_bases & HRTIMER_ACTIVE_SOFT;
|
||||
+ cpu_base->softirq_next_timer = NULL;
|
||||
+ expires_next = __hrtimer_next_event_base(cpu_base, active, KTIME_MAX);
|
||||
+
|
||||
+ next_timer = cpu_base->softirq_next_timer;
|
||||
+ }
|
||||
|
||||
- active = cpu_base->active_bases & active_mask;
|
||||
- expires_next = __hrtimer_next_event_base(cpu_base, active, expires_next);
|
||||
+ if (active_mask & HRTIMER_ACTIVE_HARD) {
|
||||
+ active = cpu_base->active_bases & HRTIMER_ACTIVE_HARD;
|
||||
+ cpu_base->next_timer = next_timer;
|
||||
+ expires_next = __hrtimer_next_event_base(cpu_base, active, expires_next);
|
||||
+ }
|
||||
|
||||
return expires_next;
|
||||
}
|
||||
-#endif
|
||||
|
||||
static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
@@ -547,8 +569,14 @@ static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base)
|
||||
ktime_t *offs_boot = &base->clock_base[HRTIMER_BASE_BOOTTIME].offset;
|
||||
ktime_t *offs_tai = &base->clock_base[HRTIMER_BASE_TAI].offset;
|
||||
|
||||
- return ktime_get_update_offsets_now(&base->clock_was_set_seq,
|
||||
+ ktime_t now = ktime_get_update_offsets_now(&base->clock_was_set_seq,
|
||||
offs_real, offs_boot, offs_tai);
|
||||
+
|
||||
+ base->clock_base[HRTIMER_BASE_REALTIME_SOFT].offset = *offs_real;
|
||||
+ base->clock_base[HRTIMER_BASE_BOOTTIME_SOFT].offset = *offs_boot;
|
||||
+ base->clock_base[HRTIMER_BASE_TAI_SOFT].offset = *offs_tai;
|
||||
+
|
||||
+ return now;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -575,7 +603,23 @@ hrtimer_force_reprogram(struct hrtimer_cpu_base *cpu_base, int skip_equal)
|
||||
{
|
||||
ktime_t expires_next;
|
||||
|
||||
- expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
+ /*
|
||||
+ * Find the current next expiration time.
|
||||
+ */
|
||||
+ expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_ALL);
|
||||
+
|
||||
+ if (cpu_base->next_timer && cpu_base->next_timer->is_soft) {
|
||||
+ /*
|
||||
+ * When the softirq is activated, hrtimer has to be
|
||||
+ * programmed with the first hard hrtimer because soft
|
||||
+ * timer interrupt could occur too late.
|
||||
+ */
|
||||
+ if (cpu_base->softirq_activated)
|
||||
+ expires_next = __hrtimer_get_next_event(cpu_base,
|
||||
+ HRTIMER_ACTIVE_HARD);
|
||||
+ else
|
||||
+ cpu_base->softirq_expires_next = expires_next;
|
||||
+ }
|
||||
|
||||
if (skip_equal && expires_next == cpu_base->expires_next)
|
||||
return;
|
||||
@@ -702,7 +746,7 @@ static inline void retrigger_next_event(void *arg) { }
|
||||
*
|
||||
* Called with interrupts disabled and base->cpu_base.lock held
|
||||
*/
|
||||
-static void hrtimer_reprogram(struct hrtimer *timer)
|
||||
+static void hrtimer_reprogram(struct hrtimer *timer, bool reprogram)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
struct hrtimer_clock_base *base = timer->base;
|
||||
@@ -710,6 +754,37 @@ static void hrtimer_reprogram(struct hrtimer *timer)
|
||||
|
||||
WARN_ON_ONCE(hrtimer_get_expires_tv64(timer) < 0);
|
||||
|
||||
+ /*
|
||||
+ * CLOCK_REALTIME timer might be requested with an absolute
|
||||
+ * expiry time which is less than base->offset. Set it to 0.
|
||||
+ */
|
||||
+ if (expires < 0)
|
||||
+ expires = 0;
|
||||
+
|
||||
+ if (timer->is_soft) {
|
||||
+ /*
|
||||
+ * soft hrtimer could be started on a remote CPU. In this
|
||||
+ * case softirq_expires_next needs to be updated on the
|
||||
+ * remote CPU. The soft hrtimer will not expire before the
|
||||
+ * first hard hrtimer on the remote CPU -
|
||||
+ * hrtimer_check_target() prevents this case.
|
||||
+ */
|
||||
+ struct hrtimer_cpu_base *timer_cpu_base = base->cpu_base;
|
||||
+
|
||||
+ if (timer_cpu_base->softirq_activated)
|
||||
+ return;
|
||||
+
|
||||
+ if (!ktime_before(expires, timer_cpu_base->softirq_expires_next))
|
||||
+ return;
|
||||
+
|
||||
+ timer_cpu_base->softirq_next_timer = timer;
|
||||
+ timer_cpu_base->softirq_expires_next = expires;
|
||||
+
|
||||
+ if (!ktime_before(expires, timer_cpu_base->expires_next) ||
|
||||
+ !reprogram)
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* If the timer is not on the current cpu, we cannot reprogram
|
||||
* the other cpus clock event device.
|
||||
@@ -727,13 +802,6 @@ static void hrtimer_reprogram(struct hrtimer *timer)
|
||||
if (cpu_base->in_hrtirq)
|
||||
return;
|
||||
|
||||
- /*
|
||||
- * CLOCK_REALTIME timer might be requested with an absolute
|
||||
- * expiry time which is less than base->offset. Set it to 0.
|
||||
- */
|
||||
- if (expires < 0)
|
||||
- expires = 0;
|
||||
-
|
||||
if (expires >= cpu_base->expires_next)
|
||||
return;
|
||||
|
||||
@@ -961,6 +1029,31 @@ static inline ktime_t hrtimer_update_lowres(struct hrtimer *timer, ktime_t tim,
|
||||
return tim;
|
||||
}
|
||||
|
||||
+static void
|
||||
+hrtimer_update_softirq_timer(struct hrtimer_cpu_base *cpu_base, bool reprogram)
|
||||
+{
|
||||
+ ktime_t expires;
|
||||
+
|
||||
+ /*
|
||||
+ * Find the next SOFT expiration.
|
||||
+ */
|
||||
+ expires = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_SOFT);
|
||||
+
|
||||
+ /*
|
||||
+ * reprogramming needs to be triggered, even if the next soft
|
||||
+ * hrtimer expires at the same time than the next hard
|
||||
+ * hrtimer. cpu_base->softirq_expires_next needs to be updated!
|
||||
+ */
|
||||
+ if (expires == KTIME_MAX)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * cpu_base->*next_timer is recomputed by __hrtimer_get_next_event()
|
||||
+ * cpu_base->*expires_next is only set by hrtimer_reprogram()
|
||||
+ */
|
||||
+ hrtimer_reprogram(cpu_base->softirq_next_timer, reprogram);
|
||||
+}
|
||||
+
|
||||
static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
u64 delta_ns, const enum hrtimer_mode mode,
|
||||
struct hrtimer_clock_base *base)
|
||||
@@ -982,13 +1075,15 @@ static int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
|
||||
return enqueue_hrtimer(timer, new_base, mode);
|
||||
}
|
||||
+
|
||||
/**
|
||||
* hrtimer_start_range_ns - (re)start an hrtimer
|
||||
* @timer: the timer to be added
|
||||
* @tim: expiry time
|
||||
* @delta_ns: "slack" range for the timer
|
||||
* @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED)
|
||||
+ * relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);
|
||||
+ * softirq based mode is considered for debug purpose only!
|
||||
*/
|
||||
void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
u64 delta_ns, const enum hrtimer_mode mode)
|
||||
@@ -996,10 +1091,16 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
||||
struct hrtimer_clock_base *base;
|
||||
unsigned long flags;
|
||||
|
||||
+ /*
|
||||
+ * Check whether the HRTIMER_MODE_SOFT bit and hrtimer.is_soft
|
||||
+ * match.
|
||||
+ */
|
||||
+ WARN_ON_ONCE(!(mode & HRTIMER_MODE_SOFT) ^ !timer->is_soft);
|
||||
+
|
||||
base = lock_hrtimer_base(timer, &flags);
|
||||
|
||||
if (__hrtimer_start_range_ns(timer, tim, delta_ns, mode, base))
|
||||
- hrtimer_reprogram(timer);
|
||||
+ hrtimer_reprogram(timer, true);
|
||||
|
||||
unlock_hrtimer_base(timer, &flags);
|
||||
}
|
||||
@@ -1098,7 +1199,7 @@ u64 hrtimer_get_next_event(void)
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
|
||||
if (!__hrtimer_hres_active(cpu_base))
|
||||
- expires = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
+ expires = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_ALL);
|
||||
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
|
||||
@@ -1308,6 +1409,23 @@ static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
|
||||
}
|
||||
}
|
||||
|
||||
+static __latent_entropy void hrtimer_run_softirq(struct softirq_action *h)
|
||||
+{
|
||||
+ struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
|
||||
+ unsigned long flags;
|
||||
+ ktime_t now;
|
||||
+
|
||||
+ raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
+
|
||||
+ now = hrtimer_update_base(cpu_base);
|
||||
+ __hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_SOFT);
|
||||
+
|
||||
+ cpu_base->softirq_activated = 0;
|
||||
+ hrtimer_update_softirq_timer(cpu_base, true);
|
||||
+
|
||||
+ raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
||||
/*
|
||||
@@ -1338,10 +1456,16 @@ void hrtimer_interrupt(struct clock_event_device *dev)
|
||||
*/
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
|
||||
+ if (!ktime_before(now, cpu_base->softirq_expires_next)) {
|
||||
+ cpu_base->softirq_expires_next = KTIME_MAX;
|
||||
+ cpu_base->softirq_activated = 1;
|
||||
+ raise_softirq_irqoff(HRTIMER_SOFTIRQ);
|
||||
+ }
|
||||
+
|
||||
__hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD);
|
||||
|
||||
/* Reevaluate the clock bases for the next expiry */
|
||||
- expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_HARD);
|
||||
+ expires_next = __hrtimer_get_next_event(cpu_base, HRTIMER_ACTIVE_ALL);
|
||||
/*
|
||||
* Store the new expiry value so the migration code can verify
|
||||
* against it.
|
||||
@@ -1445,6 +1569,13 @@ void hrtimer_run_queues(void)
|
||||
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
+
|
||||
+ if (!ktime_before(now, cpu_base->softirq_expires_next)) {
|
||||
+ cpu_base->softirq_expires_next = KTIME_MAX;
|
||||
+ cpu_base->softirq_activated = 1;
|
||||
+ raise_softirq_irqoff(HRTIMER_SOFTIRQ);
|
||||
+ }
|
||||
+
|
||||
__hrtimer_run_queues(cpu_base, now, flags, HRTIMER_ACTIVE_HARD);
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
}
|
||||
@@ -1628,7 +1759,9 @@ int hrtimers_prepare_cpu(unsigned int cpu)
|
||||
cpu_base->hres_active = 0;
|
||||
cpu_base->hang_detected = 0;
|
||||
cpu_base->next_timer = NULL;
|
||||
+ cpu_base->softirq_next_timer = NULL;
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
+ cpu_base->softirq_expires_next = KTIME_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1672,6 +1805,12 @@ int hrtimers_dead_cpu(unsigned int scpu)
|
||||
BUG_ON(cpu_online(scpu));
|
||||
tick_cancel_sched_timer(scpu);
|
||||
|
||||
+ /*
|
||||
+ * this BH disable ensures that raise_softirq_irqoff() does
|
||||
+ * not wakeup ksoftirqd (and acquire the pi-lock) while
|
||||
+ * holding the cpu_base lock
|
||||
+ */
|
||||
+ local_bh_disable();
|
||||
local_irq_disable();
|
||||
old_base = &per_cpu(hrtimer_bases, scpu);
|
||||
new_base = this_cpu_ptr(&hrtimer_bases);
|
||||
@@ -1687,12 +1826,19 @@ int hrtimers_dead_cpu(unsigned int scpu)
|
||||
&new_base->clock_base[i]);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * The migration might have changed the first expiring softirq
|
||||
+ * timer on this CPU. Update it.
|
||||
+ */
|
||||
+ hrtimer_update_softirq_timer(new_base, false);
|
||||
+
|
||||
raw_spin_unlock(&old_base->lock);
|
||||
raw_spin_unlock(&new_base->lock);
|
||||
|
||||
/* Check, if we got expired work to do */
|
||||
__hrtimer_peek_ahead_timers();
|
||||
local_irq_enable();
|
||||
+ local_bh_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1701,6 +1847,7 @@ int hrtimers_dead_cpu(unsigned int scpu)
|
||||
void __init hrtimers_init(void)
|
||||
{
|
||||
hrtimers_prepare_cpu(smp_processor_id());
|
||||
+ open_softirq(HRTIMER_SOFTIRQ, hrtimer_run_softirq);
|
||||
}
|
||||
|
||||
/**
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
From 236c74d642c5fb89d4733b97b4f85ba6d1f98fde Mon Sep 17 00:00:00 2001
|
||||
From: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Date: Wed, 20 Dec 2017 17:13:18 +0100
|
||||
Subject: [PATCH 043/450] hrtimer: Implement SOFT/HARD clock base selection
|
||||
|
||||
All prerequisites to handle hrtimers for expiry in either hard or soft
|
||||
interrupt context are in place.
|
||||
|
||||
Add the missing bit in hrtimer_init() which associates the timer to the
|
||||
hard or the soft irq clock base.
|
||||
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/time/hrtimer.c | 15 +++++++++++----
|
||||
1 file changed, 11 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
|
||||
index 66d7a12d7256..9947480ad731 100644
|
||||
--- a/kernel/time/hrtimer.c
|
||||
+++ b/kernel/time/hrtimer.c
|
||||
@@ -1222,8 +1222,9 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id)
|
||||
static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
+ bool softtimer = !!(mode & HRTIMER_MODE_SOFT);
|
||||
+ int base = softtimer ? HRTIMER_MAX_CLOCK_BASES / 2 : 0;
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
- int base;
|
||||
|
||||
memset(timer, 0, sizeof(struct hrtimer));
|
||||
|
||||
@@ -1237,7 +1238,8 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
if (clock_id == CLOCK_REALTIME && mode & HRTIMER_MODE_REL)
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
|
||||
- base = hrtimer_clockid_to_base(clock_id);
|
||||
+ base += hrtimer_clockid_to_base(clock_id);
|
||||
+ timer->is_soft = softtimer;
|
||||
timer->base = &cpu_base->clock_base[base];
|
||||
timerqueue_init(&timer->node);
|
||||
}
|
||||
@@ -1246,8 +1248,13 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
* hrtimer_init - initialize a timer to the given clock
|
||||
* @timer: the timer to be initialized
|
||||
* @clock_id: the clock to be used
|
||||
- * @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
|
||||
- * relative (HRTIMER_MODE_REL); pinned is not considered here!
|
||||
+ * @mode: The modes which are relevant for intitialization:
|
||||
+ * HRTIMER_MODE_ABS, HRTIMER_MODE_REL, HRTIMER_MODE_ABS_SOFT,
|
||||
+ * HRTIMER_MODE_REL_SOFT
|
||||
+ *
|
||||
+ * The PINNED variants of the above can be handed in,
|
||||
+ * but the PINNED bit is ignored as pinning happens
|
||||
+ * when the hrtimer is started
|
||||
*/
|
||||
void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,321 +0,0 @@
|
||||
From 53707523d5193b8dd4a0fb417bf4a09edc811c6c Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:11 +0100
|
||||
Subject: [PATCH 044/450] can/bcm: Replace hrtimer_tasklet with softirq based
|
||||
hrtimer
|
||||
|
||||
Switch the timer to HRTIMER_MODE_SOFT, which executed the timer
|
||||
callback in softirq context and remove the hrtimer_tasklet.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Cc: Oliver Hartkopp <socketcan@hartkopp.net>
|
||||
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
|
||||
Cc: linux-can@vger.kernel.org
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
net/can/bcm.c | 156 +++++++++++++++++---------------------------------
|
||||
1 file changed, 52 insertions(+), 104 deletions(-)
|
||||
|
||||
diff --git a/net/can/bcm.c b/net/can/bcm.c
|
||||
index 13690334efa3..9cc67ac257f1 100644
|
||||
--- a/net/can/bcm.c
|
||||
+++ b/net/can/bcm.c
|
||||
@@ -102,7 +102,6 @@ struct bcm_op {
|
||||
unsigned long frames_abs, frames_filtered;
|
||||
struct bcm_timeval ival1, ival2;
|
||||
struct hrtimer timer, thrtimer;
|
||||
- struct tasklet_struct tsklet, thrtsklet;
|
||||
ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
|
||||
int rx_ifindex;
|
||||
int cfsiz;
|
||||
@@ -364,25 +363,34 @@ static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
|
||||
}
|
||||
}
|
||||
|
||||
-static void bcm_tx_start_timer(struct bcm_op *op)
|
||||
+static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt)
|
||||
{
|
||||
+ ktime_t ival;
|
||||
+
|
||||
if (op->kt_ival1 && op->count)
|
||||
- hrtimer_start(&op->timer,
|
||||
- ktime_add(ktime_get(), op->kt_ival1),
|
||||
- HRTIMER_MODE_ABS);
|
||||
+ ival = op->kt_ival1;
|
||||
else if (op->kt_ival2)
|
||||
- hrtimer_start(&op->timer,
|
||||
- ktime_add(ktime_get(), op->kt_ival2),
|
||||
- HRTIMER_MODE_ABS);
|
||||
+ ival = op->kt_ival2;
|
||||
+ else
|
||||
+ return false;
|
||||
+
|
||||
+ hrtimer_set_expires(hrt, ktime_add(ktime_get(), ival));
|
||||
+ return true;
|
||||
}
|
||||
|
||||
-static void bcm_tx_timeout_tsklet(unsigned long data)
|
||||
+static void bcm_tx_start_timer(struct bcm_op *op)
|
||||
{
|
||||
- struct bcm_op *op = (struct bcm_op *)data;
|
||||
+ if (bcm_tx_set_expiry(op, &op->timer))
|
||||
+ hrtimer_start_expires(&op->timer, HRTIMER_MODE_ABS_SOFT);
|
||||
+}
|
||||
+
|
||||
+/* bcm_tx_timeout_handler - performs cyclic CAN frame transmissions */
|
||||
+static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
|
||||
+{
|
||||
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
|
||||
struct bcm_msg_head msg_head;
|
||||
|
||||
if (op->kt_ival1 && (op->count > 0)) {
|
||||
-
|
||||
op->count--;
|
||||
if (!op->count && (op->flags & TX_COUNTEVT)) {
|
||||
|
||||
@@ -399,22 +407,12 @@ static void bcm_tx_timeout_tsklet(unsigned long data)
|
||||
}
|
||||
bcm_can_tx(op);
|
||||
|
||||
- } else if (op->kt_ival2)
|
||||
+ } else if (op->kt_ival2) {
|
||||
bcm_can_tx(op);
|
||||
+ }
|
||||
|
||||
- bcm_tx_start_timer(op);
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * bcm_tx_timeout_handler - performs cyclic CAN frame transmissions
|
||||
- */
|
||||
-static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
|
||||
-{
|
||||
- struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
|
||||
-
|
||||
- tasklet_schedule(&op->tsklet);
|
||||
-
|
||||
- return HRTIMER_NORESTART;
|
||||
+ return bcm_tx_set_expiry(op, &op->timer) ?
|
||||
+ HRTIMER_RESTART : HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -480,7 +478,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
|
||||
/* do not send the saved data - only start throttle timer */
|
||||
hrtimer_start(&op->thrtimer,
|
||||
ktime_add(op->kt_lastmsg, op->kt_ival2),
|
||||
- HRTIMER_MODE_ABS);
|
||||
+ HRTIMER_MODE_ABS_SOFT);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -539,14 +537,21 @@ static void bcm_rx_starttimer(struct bcm_op *op)
|
||||
return;
|
||||
|
||||
if (op->kt_ival1)
|
||||
- hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&op->timer, op->kt_ival1, HRTIMER_MODE_REL_SOFT);
|
||||
}
|
||||
|
||||
-static void bcm_rx_timeout_tsklet(unsigned long data)
|
||||
+/* bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out */
|
||||
+static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
|
||||
{
|
||||
- struct bcm_op *op = (struct bcm_op *)data;
|
||||
+ struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
|
||||
struct bcm_msg_head msg_head;
|
||||
|
||||
+ /* if user wants to be informed, when cyclic CAN-Messages come back */
|
||||
+ if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
|
||||
+ /* clear received CAN frames to indicate 'nothing received' */
|
||||
+ memset(op->last_frames, 0, op->nframes * op->cfsiz);
|
||||
+ }
|
||||
+
|
||||
/* create notification to user */
|
||||
msg_head.opcode = RX_TIMEOUT;
|
||||
msg_head.flags = op->flags;
|
||||
@@ -557,25 +562,6 @@ static void bcm_rx_timeout_tsklet(unsigned long data)
|
||||
msg_head.nframes = 0;
|
||||
|
||||
bcm_send_to_user(op, &msg_head, NULL, 0);
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out
|
||||
- */
|
||||
-static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
|
||||
-{
|
||||
- struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);
|
||||
-
|
||||
- /* schedule before NET_RX_SOFTIRQ */
|
||||
- tasklet_hi_schedule(&op->tsklet);
|
||||
-
|
||||
- /* no restart of the timer is done here! */
|
||||
-
|
||||
- /* if user wants to be informed, when cyclic CAN-Messages come back */
|
||||
- if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
|
||||
- /* clear received CAN frames to indicate 'nothing received' */
|
||||
- memset(op->last_frames, 0, op->nframes * op->cfsiz);
|
||||
- }
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
@@ -583,14 +569,12 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
|
||||
/*
|
||||
* bcm_rx_do_flush - helper for bcm_rx_thr_flush
|
||||
*/
|
||||
-static inline int bcm_rx_do_flush(struct bcm_op *op, int update,
|
||||
- unsigned int index)
|
||||
+static inline int bcm_rx_do_flush(struct bcm_op *op, unsigned int index)
|
||||
{
|
||||
struct canfd_frame *lcf = op->last_frames + op->cfsiz * index;
|
||||
|
||||
if ((op->last_frames) && (lcf->flags & RX_THR)) {
|
||||
- if (update)
|
||||
- bcm_rx_changed(op, lcf);
|
||||
+ bcm_rx_changed(op, lcf);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -598,11 +582,8 @@ static inline int bcm_rx_do_flush(struct bcm_op *op, int update,
|
||||
|
||||
/*
|
||||
* bcm_rx_thr_flush - Check for throttled data and send it to the userspace
|
||||
- *
|
||||
- * update == 0 : just check if throttled data is available (any irq context)
|
||||
- * update == 1 : check and send throttled data to userspace (soft_irq context)
|
||||
*/
|
||||
-static int bcm_rx_thr_flush(struct bcm_op *op, int update)
|
||||
+static int bcm_rx_thr_flush(struct bcm_op *op)
|
||||
{
|
||||
int updated = 0;
|
||||
|
||||
@@ -611,24 +592,16 @@ static int bcm_rx_thr_flush(struct bcm_op *op, int update)
|
||||
|
||||
/* for MUX filter we start at index 1 */
|
||||
for (i = 1; i < op->nframes; i++)
|
||||
- updated += bcm_rx_do_flush(op, update, i);
|
||||
+ updated += bcm_rx_do_flush(op, i);
|
||||
|
||||
} else {
|
||||
/* for RX_FILTER_ID and simple filter */
|
||||
- updated += bcm_rx_do_flush(op, update, 0);
|
||||
+ updated += bcm_rx_do_flush(op, 0);
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
-static void bcm_rx_thr_tsklet(unsigned long data)
|
||||
-{
|
||||
- struct bcm_op *op = (struct bcm_op *)data;
|
||||
-
|
||||
- /* push the changed data to the userspace */
|
||||
- bcm_rx_thr_flush(op, 1);
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* bcm_rx_thr_handler - the time for blocked content updates is over now:
|
||||
* Check for throttled data and send it to the userspace
|
||||
@@ -637,9 +610,7 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
|
||||
{
|
||||
struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer);
|
||||
|
||||
- tasklet_schedule(&op->thrtsklet);
|
||||
-
|
||||
- if (bcm_rx_thr_flush(op, 0)) {
|
||||
+ if (bcm_rx_thr_flush(op)) {
|
||||
hrtimer_forward(hrtimer, ktime_get(), op->kt_ival2);
|
||||
return HRTIMER_RESTART;
|
||||
} else {
|
||||
@@ -735,23 +706,8 @@ static struct bcm_op *bcm_find_op(struct list_head *ops,
|
||||
|
||||
static void bcm_remove_op(struct bcm_op *op)
|
||||
{
|
||||
- if (op->tsklet.func) {
|
||||
- while (test_bit(TASKLET_STATE_SCHED, &op->tsklet.state) ||
|
||||
- test_bit(TASKLET_STATE_RUN, &op->tsklet.state) ||
|
||||
- hrtimer_active(&op->timer)) {
|
||||
- hrtimer_cancel(&op->timer);
|
||||
- tasklet_kill(&op->tsklet);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (op->thrtsklet.func) {
|
||||
- while (test_bit(TASKLET_STATE_SCHED, &op->thrtsklet.state) ||
|
||||
- test_bit(TASKLET_STATE_RUN, &op->thrtsklet.state) ||
|
||||
- hrtimer_active(&op->thrtimer)) {
|
||||
- hrtimer_cancel(&op->thrtimer);
|
||||
- tasklet_kill(&op->thrtsklet);
|
||||
- }
|
||||
- }
|
||||
+ hrtimer_cancel(&op->timer);
|
||||
+ hrtimer_cancel(&op->thrtimer);
|
||||
|
||||
if ((op->frames) && (op->frames != &op->sframe))
|
||||
kfree(op->frames);
|
||||
@@ -979,15 +935,13 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
||||
op->ifindex = ifindex;
|
||||
|
||||
/* initialize uninitialized (kzalloc) structure */
|
||||
- hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&op->timer, CLOCK_MONOTONIC,
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
op->timer.function = bcm_tx_timeout_handler;
|
||||
|
||||
- /* initialize tasklet for tx countevent notification */
|
||||
- tasklet_init(&op->tsklet, bcm_tx_timeout_tsklet,
|
||||
- (unsigned long) op);
|
||||
-
|
||||
/* currently unused in tx_ops */
|
||||
- hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
/* add this bcm_op to the list of the tx_ops */
|
||||
list_add(&op->list, &bo->tx_ops);
|
||||
@@ -1150,20 +1104,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
||||
op->rx_ifindex = ifindex;
|
||||
|
||||
/* initialize uninitialized (kzalloc) structure */
|
||||
- hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&op->timer, CLOCK_MONOTONIC,
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
op->timer.function = bcm_rx_timeout_handler;
|
||||
|
||||
- /* initialize tasklet for rx timeout notification */
|
||||
- tasklet_init(&op->tsklet, bcm_rx_timeout_tsklet,
|
||||
- (unsigned long) op);
|
||||
-
|
||||
- hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&op->thrtimer, CLOCK_MONOTONIC,
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
op->thrtimer.function = bcm_rx_thr_handler;
|
||||
|
||||
- /* initialize tasklet for rx throttle handling */
|
||||
- tasklet_init(&op->thrtsklet, bcm_rx_thr_tsklet,
|
||||
- (unsigned long) op);
|
||||
-
|
||||
/* add this bcm_op to the list of the rx_ops */
|
||||
list_add(&op->list, &bo->rx_ops);
|
||||
|
||||
@@ -1209,12 +1157,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
||||
*/
|
||||
op->kt_lastmsg = 0;
|
||||
hrtimer_cancel(&op->thrtimer);
|
||||
- bcm_rx_thr_flush(op, 1);
|
||||
+ bcm_rx_thr_flush(op);
|
||||
}
|
||||
|
||||
if ((op->flags & STARTTIMER) && op->kt_ival1)
|
||||
hrtimer_start(&op->timer, op->kt_ival1,
|
||||
- HRTIMER_MODE_REL);
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
}
|
||||
|
||||
/* now we can register for can_ids, if we added a new bcm_op */
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
From 342c4c04e0060c8e398dad26f9ca0abe9f9985b8 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:12 +0100
|
||||
Subject: [PATCH 045/450] mac80211_hwsim: Replace hrtimer tasklet with softirq
|
||||
hrtimer
|
||||
|
||||
Switch the timer to HRTIMER_MODE_SOFT, which executed the timer
|
||||
callback in softirq context and remove the hrtimer_tasklet.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Cc: linux-wireless@vger.kernel.org
|
||||
Cc: Johannes Berg <johannes@sipsolutions.net>
|
||||
Cc: Kalle Valo <kvalo@codeaurora.org>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/net/wireless/mac80211_hwsim.c | 44 ++++++++++++---------------
|
||||
1 file changed, 20 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
|
||||
index 477f9f2f6626..06dff45037fe 100644
|
||||
--- a/drivers/net/wireless/mac80211_hwsim.c
|
||||
+++ b/drivers/net/wireless/mac80211_hwsim.c
|
||||
@@ -537,7 +537,7 @@ struct mac80211_hwsim_data {
|
||||
unsigned int rx_filter;
|
||||
bool started, idle, scanning;
|
||||
struct mutex mutex;
|
||||
- struct tasklet_hrtimer beacon_timer;
|
||||
+ struct hrtimer beacon_timer;
|
||||
enum ps_mode {
|
||||
PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
|
||||
} ps;
|
||||
@@ -1423,7 +1423,7 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = hw->priv;
|
||||
data->started = false;
|
||||
- tasklet_hrtimer_cancel(&data->beacon_timer);
|
||||
+ hrtimer_cancel(&data->beacon_timer);
|
||||
wiphy_debug(hw->wiphy, "%s\n", __func__);
|
||||
}
|
||||
|
||||
@@ -1546,14 +1546,12 @@ static enum hrtimer_restart
|
||||
mac80211_hwsim_beacon(struct hrtimer *timer)
|
||||
{
|
||||
struct mac80211_hwsim_data *data =
|
||||
- container_of(timer, struct mac80211_hwsim_data,
|
||||
- beacon_timer.timer);
|
||||
+ container_of(timer, struct mac80211_hwsim_data, beacon_timer);
|
||||
struct ieee80211_hw *hw = data->hw;
|
||||
u64 bcn_int = data->beacon_int;
|
||||
- ktime_t next_bcn;
|
||||
|
||||
if (!data->started)
|
||||
- goto out;
|
||||
+ return HRTIMER_NORESTART;
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
@@ -1565,11 +1563,9 @@ mac80211_hwsim_beacon(struct hrtimer *timer)
|
||||
data->bcn_delta = 0;
|
||||
}
|
||||
|
||||
- next_bcn = ktime_add(hrtimer_get_expires(timer),
|
||||
- ns_to_ktime(bcn_int * 1000));
|
||||
- tasklet_hrtimer_start(&data->beacon_timer, next_bcn, HRTIMER_MODE_ABS);
|
||||
-out:
|
||||
- return HRTIMER_NORESTART;
|
||||
+ hrtimer_forward(&data->beacon_timer, hrtimer_get_expires(timer),
|
||||
+ ns_to_ktime(bcn_int * NSEC_PER_USEC));
|
||||
+ return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static const char * const hwsim_chanwidths[] = {
|
||||
@@ -1643,15 +1639,15 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
if (!data->started || !data->beacon_int)
|
||||
- tasklet_hrtimer_cancel(&data->beacon_timer);
|
||||
- else if (!hrtimer_is_queued(&data->beacon_timer.timer)) {
|
||||
+ hrtimer_cancel(&data->beacon_timer);
|
||||
+ else if (!hrtimer_is_queued(&data->beacon_timer)) {
|
||||
u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
|
||||
u32 bcn_int = data->beacon_int;
|
||||
u64 until_tbtt = bcn_int - do_div(tsf, bcn_int);
|
||||
|
||||
- tasklet_hrtimer_start(&data->beacon_timer,
|
||||
- ns_to_ktime(until_tbtt * 1000),
|
||||
- HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&data->beacon_timer,
|
||||
+ ns_to_ktime(until_tbtt * 1000),
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1714,7 +1710,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
info->enable_beacon, info->beacon_int);
|
||||
vp->bcn_en = info->enable_beacon;
|
||||
if (data->started &&
|
||||
- !hrtimer_is_queued(&data->beacon_timer.timer) &&
|
||||
+ !hrtimer_is_queued(&data->beacon_timer) &&
|
||||
info->enable_beacon) {
|
||||
u64 tsf, until_tbtt;
|
||||
u32 bcn_int;
|
||||
@@ -1722,9 +1718,9 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
tsf = mac80211_hwsim_get_tsf(hw, vif);
|
||||
bcn_int = data->beacon_int;
|
||||
until_tbtt = bcn_int - do_div(tsf, bcn_int);
|
||||
- tasklet_hrtimer_start(&data->beacon_timer,
|
||||
- ns_to_ktime(until_tbtt * 1000),
|
||||
- HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&data->beacon_timer,
|
||||
+ ns_to_ktime(until_tbtt * 1000),
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
} else if (!info->enable_beacon) {
|
||||
unsigned int count = 0;
|
||||
ieee80211_iterate_active_interfaces_atomic(
|
||||
@@ -1733,7 +1729,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u",
|
||||
count);
|
||||
if (count == 0) {
|
||||
- tasklet_hrtimer_cancel(&data->beacon_timer);
|
||||
+ hrtimer_cancel(&data->beacon_timer);
|
||||
data->beacon_int = 0;
|
||||
}
|
||||
}
|
||||
@@ -2722,9 +2718,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
data->debugfs,
|
||||
data, &hwsim_simulate_radar);
|
||||
|
||||
- tasklet_hrtimer_init(&data->beacon_timer,
|
||||
- mac80211_hwsim_beacon,
|
||||
- CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
+ hrtimer_init(&data->beacon_timer, CLOCK_MONOTONIC,
|
||||
+ HRTIMER_MODE_ABS_SOFT);
|
||||
+ data->beacon_timer.function = mac80211_hwsim_beacon;
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
list_add_tail(&data->list, &hwsim_radios);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
From a18cdb53cd5213258d37db709c0af9feb3e68341 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:13 +0100
|
||||
Subject: [PATCH 046/450] xfrm: Replace hrtimer tasklet with softirq hrtimer
|
||||
|
||||
Switch the timer to HRTIMER_MODE_SOFT, which executed the timer
|
||||
callback in softirq context and remove the hrtimer_tasklet.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Cc: Steffen Klassert <steffen.klassert@secunet.com>
|
||||
Cc: netdev@vger.kernel.org
|
||||
Cc: Herbert Xu <herbert@gondor.apana.org.au>
|
||||
Cc: "David S. Miller" <davem@davemloft.net>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/net/xfrm.h | 2 +-
|
||||
net/xfrm/xfrm_state.c | 30 ++++++++++++++++++------------
|
||||
2 files changed, 19 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
|
||||
index db99efb2d1d0..a7b95ffbbf8b 100644
|
||||
--- a/include/net/xfrm.h
|
||||
+++ b/include/net/xfrm.h
|
||||
@@ -217,7 +217,7 @@ struct xfrm_state {
|
||||
struct xfrm_stats stats;
|
||||
|
||||
struct xfrm_lifetime_cur curlft;
|
||||
- struct tasklet_hrtimer mtimer;
|
||||
+ struct hrtimer mtimer;
|
||||
|
||||
struct xfrm_state_offload xso;
|
||||
|
||||
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
|
||||
index 6c4ec69e11a0..77f52dc790ec 100644
|
||||
--- a/net/xfrm/xfrm_state.c
|
||||
+++ b/net/xfrm/xfrm_state.c
|
||||
@@ -427,7 +427,7 @@ static void xfrm_put_mode(struct xfrm_mode *mode)
|
||||
|
||||
static void xfrm_state_gc_destroy(struct xfrm_state *x)
|
||||
{
|
||||
- tasklet_hrtimer_cancel(&x->mtimer);
|
||||
+ hrtimer_cancel(&x->mtimer);
|
||||
del_timer_sync(&x->rtimer);
|
||||
kfree(x->aead);
|
||||
kfree(x->aalg);
|
||||
@@ -472,8 +472,8 @@ static void xfrm_state_gc_task(struct work_struct *work)
|
||||
|
||||
static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
|
||||
{
|
||||
- struct tasklet_hrtimer *thr = container_of(me, struct tasklet_hrtimer, timer);
|
||||
- struct xfrm_state *x = container_of(thr, struct xfrm_state, mtimer);
|
||||
+ struct xfrm_state *x = container_of(me, struct xfrm_state, mtimer);
|
||||
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
|
||||
unsigned long now = get_seconds();
|
||||
long next = LONG_MAX;
|
||||
int warn = 0;
|
||||
@@ -537,7 +537,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
|
||||
km_state_expired(x, 0, 0);
|
||||
resched:
|
||||
if (next != LONG_MAX) {
|
||||
- tasklet_hrtimer_start(&x->mtimer, ktime_set(next, 0), HRTIMER_MODE_REL);
|
||||
+ hrtimer_forward_now(&x->mtimer, ktime_set(next, 0));
|
||||
+ ret = HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
goto out;
|
||||
@@ -554,7 +555,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
|
||||
|
||||
out:
|
||||
spin_unlock(&x->lock);
|
||||
- return HRTIMER_NORESTART;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static void xfrm_replay_timer_handler(unsigned long data);
|
||||
@@ -573,8 +574,8 @@ struct xfrm_state *xfrm_state_alloc(struct net *net)
|
||||
INIT_HLIST_NODE(&x->bydst);
|
||||
INIT_HLIST_NODE(&x->bysrc);
|
||||
INIT_HLIST_NODE(&x->byspi);
|
||||
- tasklet_hrtimer_init(&x->mtimer, xfrm_timer_handler,
|
||||
- CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
|
||||
+ hrtimer_init(&x->mtimer, CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
|
||||
+ x->mtimer.function = xfrm_timer_handler;
|
||||
setup_timer(&x->rtimer, xfrm_replay_timer_handler,
|
||||
(unsigned long)x);
|
||||
x->curlft.add_time = get_seconds();
|
||||
@@ -1031,7 +1032,9 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
|
||||
}
|
||||
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
|
||||
- tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&x->mtimer,
|
||||
+ ktime_set(net->xfrm.sysctl_acq_expires, 0),
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
net->xfrm.state_num++;
|
||||
xfrm_hash_grow_check(net, x->bydst.next != NULL);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
@@ -1142,7 +1145,7 @@ static void __xfrm_state_insert(struct xfrm_state *x)
|
||||
hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
|
||||
}
|
||||
|
||||
- tasklet_hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
|
||||
if (x->replay_maxage)
|
||||
mod_timer(&x->rtimer, jiffies + x->replay_maxage);
|
||||
|
||||
@@ -1246,7 +1249,9 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
x->mark.m = m->m;
|
||||
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
|
||||
xfrm_state_hold(x);
|
||||
- tasklet_hrtimer_start(&x->mtimer, ktime_set(net->xfrm.sysctl_acq_expires, 0), HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&x->mtimer,
|
||||
+ ktime_set(net->xfrm.sysctl_acq_expires, 0),
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
list_add(&x->km.all, &net->xfrm.state_all);
|
||||
hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
|
||||
h = xfrm_src_hash(net, daddr, saddr, family);
|
||||
@@ -1546,7 +1551,8 @@ int xfrm_state_update(struct xfrm_state *x)
|
||||
memcpy(&x1->lft, &x->lft, sizeof(x1->lft));
|
||||
x1->km.dying = 0;
|
||||
|
||||
- tasklet_hrtimer_start(&x1->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&x1->mtimer, ktime_set(1, 0),
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
if (x1->curlft.use_time)
|
||||
xfrm_state_check_expire(x1);
|
||||
|
||||
@@ -1570,7 +1576,7 @@ int xfrm_state_check_expire(struct xfrm_state *x)
|
||||
if (x->curlft.bytes >= x->lft.hard_byte_limit ||
|
||||
x->curlft.packets >= x->lft.hard_packet_limit) {
|
||||
x->km.state = XFRM_STATE_EXPIRED;
|
||||
- tasklet_hrtimer_start(&x->mtimer, 0, HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&x->mtimer, 0, HRTIMER_MODE_REL_SOFT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
From 7e36685a07adb7a6bb5e1c8ca09b359dad8ba444 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:14 +0100
|
||||
Subject: [PATCH 047/450] softirq: Remove tasklet_hrtimer
|
||||
|
||||
There are no more tasklet_hrtimer users of this interface.
|
||||
Remove it.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/interrupt.h | 25 -------------------
|
||||
kernel/softirq.c | 51 ---------------------------------------
|
||||
2 files changed, 76 deletions(-)
|
||||
|
||||
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
|
||||
index 69c238210325..af15bfd89598 100644
|
||||
--- a/include/linux/interrupt.h
|
||||
+++ b/include/linux/interrupt.h
|
||||
@@ -618,31 +618,6 @@ extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);
|
||||
extern void tasklet_init(struct tasklet_struct *t,
|
||||
void (*func)(unsigned long), unsigned long data);
|
||||
|
||||
-struct tasklet_hrtimer {
|
||||
- struct hrtimer timer;
|
||||
- struct tasklet_struct tasklet;
|
||||
- enum hrtimer_restart (*function)(struct hrtimer *);
|
||||
-};
|
||||
-
|
||||
-extern void
|
||||
-tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer,
|
||||
- enum hrtimer_restart (*function)(struct hrtimer *),
|
||||
- clockid_t which_clock, enum hrtimer_mode mode);
|
||||
-
|
||||
-static inline
|
||||
-void tasklet_hrtimer_start(struct tasklet_hrtimer *ttimer, ktime_t time,
|
||||
- const enum hrtimer_mode mode)
|
||||
-{
|
||||
- hrtimer_start(&ttimer->timer, time, mode);
|
||||
-}
|
||||
-
|
||||
-static inline
|
||||
-void tasklet_hrtimer_cancel(struct tasklet_hrtimer *ttimer)
|
||||
-{
|
||||
- hrtimer_cancel(&ttimer->timer);
|
||||
- tasklet_kill(&ttimer->tasklet);
|
||||
-}
|
||||
-
|
||||
/*
|
||||
* Autoprobing for irqs:
|
||||
*
|
||||
diff --git a/kernel/softirq.c b/kernel/softirq.c
|
||||
index a4c87cf27f9d..1a5dfc8dcf49 100644
|
||||
--- a/kernel/softirq.c
|
||||
+++ b/kernel/softirq.c
|
||||
@@ -588,57 +588,6 @@ void tasklet_kill(struct tasklet_struct *t)
|
||||
}
|
||||
EXPORT_SYMBOL(tasklet_kill);
|
||||
|
||||
-/*
|
||||
- * tasklet_hrtimer
|
||||
- */
|
||||
-
|
||||
-/*
|
||||
- * The trampoline is called when the hrtimer expires. It schedules a tasklet
|
||||
- * to run __tasklet_hrtimer_trampoline() which in turn will call the intended
|
||||
- * hrtimer callback, but from softirq context.
|
||||
- */
|
||||
-static enum hrtimer_restart __hrtimer_tasklet_trampoline(struct hrtimer *timer)
|
||||
-{
|
||||
- struct tasklet_hrtimer *ttimer =
|
||||
- container_of(timer, struct tasklet_hrtimer, timer);
|
||||
-
|
||||
- tasklet_hi_schedule(&ttimer->tasklet);
|
||||
- return HRTIMER_NORESTART;
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * Helper function which calls the hrtimer callback from
|
||||
- * tasklet/softirq context
|
||||
- */
|
||||
-static void __tasklet_hrtimer_trampoline(unsigned long data)
|
||||
-{
|
||||
- struct tasklet_hrtimer *ttimer = (void *)data;
|
||||
- enum hrtimer_restart restart;
|
||||
-
|
||||
- restart = ttimer->function(&ttimer->timer);
|
||||
- if (restart != HRTIMER_NORESTART)
|
||||
- hrtimer_restart(&ttimer->timer);
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * tasklet_hrtimer_init - Init a tasklet/hrtimer combo for softirq callbacks
|
||||
- * @ttimer: tasklet_hrtimer which is initialized
|
||||
- * @function: hrtimer callback function which gets called from softirq context
|
||||
- * @which_clock: clock id (CLOCK_MONOTONIC/CLOCK_REALTIME)
|
||||
- * @mode: hrtimer mode (HRTIMER_MODE_ABS/HRTIMER_MODE_REL)
|
||||
- */
|
||||
-void tasklet_hrtimer_init(struct tasklet_hrtimer *ttimer,
|
||||
- enum hrtimer_restart (*function)(struct hrtimer *),
|
||||
- clockid_t which_clock, enum hrtimer_mode mode)
|
||||
-{
|
||||
- hrtimer_init(&ttimer->timer, which_clock, mode);
|
||||
- ttimer->timer.function = __hrtimer_tasklet_trampoline;
|
||||
- tasklet_init(&ttimer->tasklet, __tasklet_hrtimer_trampoline,
|
||||
- (unsigned long)ttimer);
|
||||
- ttimer->function = function;
|
||||
-}
|
||||
-EXPORT_SYMBOL_GPL(tasklet_hrtimer_init);
|
||||
-
|
||||
void __init softirq_init(void)
|
||||
{
|
||||
int cpu;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
From 3ebe79e022f917a7461c7785d2882e578c34f094 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:15 +0100
|
||||
Subject: [PATCH 048/450] ALSA/dummy: Replace tasklet with softirq hrtimer
|
||||
|
||||
The tasklet is used to defer the execution of snd_pcm_period_elapsed() to
|
||||
the softirq context. Using the HRTIMER_MODE_SOFT mode invokes the timer
|
||||
callback in softirq context as well which renders the tasklet useless.
|
||||
|
||||
[o-takashi: avoid stall due to a call of hrtimer_cancel() on a callback
|
||||
of hrtimer]
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Cc: alsa-devel@alsa-project.org
|
||||
Cc: Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
||||
Cc: Takashi Iwai <tiwai@suse.com>
|
||||
Cc: Jaroslav Kysela <perex@perex.cz>
|
||||
Link: http://lkml.kernel.org/r/20170905161820.jtysvxtfleunbbmf@breakpoint.cc
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
sound/drivers/dummy.c | 27 ++++++++++++---------------
|
||||
1 file changed, 12 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
|
||||
index c0939a0164a6..549e014ecc0d 100644
|
||||
--- a/sound/drivers/dummy.c
|
||||
+++ b/sound/drivers/dummy.c
|
||||
@@ -376,17 +376,9 @@ struct dummy_hrtimer_pcm {
|
||||
ktime_t period_time;
|
||||
atomic_t running;
|
||||
struct hrtimer timer;
|
||||
- struct tasklet_struct tasklet;
|
||||
struct snd_pcm_substream *substream;
|
||||
};
|
||||
|
||||
-static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
|
||||
-{
|
||||
- struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
|
||||
- if (atomic_read(&dpcm->running))
|
||||
- snd_pcm_period_elapsed(dpcm->substream);
|
||||
-}
|
||||
-
|
||||
static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
|
||||
{
|
||||
struct dummy_hrtimer_pcm *dpcm;
|
||||
@@ -394,7 +386,14 @@ static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
|
||||
dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
|
||||
if (!atomic_read(&dpcm->running))
|
||||
return HRTIMER_NORESTART;
|
||||
- tasklet_schedule(&dpcm->tasklet);
|
||||
+ /*
|
||||
+ * In cases of XRUN and draining, this calls .trigger to stop PCM
|
||||
+ * substream.
|
||||
+ */
|
||||
+ snd_pcm_period_elapsed(dpcm->substream);
|
||||
+ if (!atomic_read(&dpcm->running))
|
||||
+ return HRTIMER_NORESTART;
|
||||
+
|
||||
hrtimer_forward_now(timer, dpcm->period_time);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
@@ -404,7 +403,7 @@ static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
|
||||
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
|
||||
|
||||
dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
|
||||
- hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
|
||||
+ hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL_SOFT);
|
||||
atomic_set(&dpcm->running, 1);
|
||||
return 0;
|
||||
}
|
||||
@@ -414,14 +413,14 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
|
||||
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
|
||||
|
||||
atomic_set(&dpcm->running, 0);
|
||||
- hrtimer_cancel(&dpcm->timer);
|
||||
+ if (!hrtimer_callback_running(&dpcm->timer))
|
||||
+ hrtimer_cancel(&dpcm->timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
|
||||
{
|
||||
hrtimer_cancel(&dpcm->timer);
|
||||
- tasklet_kill(&dpcm->tasklet);
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
@@ -466,12 +465,10 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
|
||||
if (!dpcm)
|
||||
return -ENOMEM;
|
||||
substream->runtime->private_data = dpcm;
|
||||
- hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
dpcm->timer.function = dummy_hrtimer_callback;
|
||||
dpcm->substream = substream;
|
||||
atomic_set(&dpcm->running, 0);
|
||||
- tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
|
||||
- (unsigned long)dpcm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
From 9e2909d68eb94cf781688f66472b85e95a8aebde Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:16 +0100
|
||||
Subject: [PATCH 049/450] usb/gadget/NCM: Replace tasklet with softirq hrtimer
|
||||
|
||||
The tx_tasklet tasklet is used in invoke the hrtimer (task_timer) in
|
||||
softirq context. This can be also achieved without the tasklet but
|
||||
with HRTIMER_MODE_SOFT as hrtimer mode.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Cc: Felipe Balbi <balbi@kernel.org>
|
||||
Cc: linux-usb@vger.kernel.org
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/usb/gadget/function/f_ncm.c | 30 +++++++----------------------
|
||||
1 file changed, 7 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
|
||||
index 45b334ceaf2e..5f24e6d3b6eb 100644
|
||||
--- a/drivers/usb/gadget/function/f_ncm.c
|
||||
+++ b/drivers/usb/gadget/function/f_ncm.c
|
||||
@@ -77,9 +77,7 @@ struct f_ncm {
|
||||
struct sk_buff *skb_tx_ndp;
|
||||
u16 ndp_dgram_count;
|
||||
bool timer_force_tx;
|
||||
- struct tasklet_struct tx_tasklet;
|
||||
struct hrtimer task_timer;
|
||||
-
|
||||
bool timer_stopping;
|
||||
};
|
||||
|
||||
@@ -1108,7 +1106,7 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
||||
|
||||
/* Delay the timer. */
|
||||
hrtimer_start(&ncm->task_timer, TX_TIMEOUT_NSECS,
|
||||
- HRTIMER_MODE_REL);
|
||||
+ HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
/* Add the datagram position entries */
|
||||
ntb_ndp = skb_put_zero(ncm->skb_tx_ndp, dgram_idx_len);
|
||||
@@ -1152,17 +1150,15 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
|
||||
}
|
||||
|
||||
/*
|
||||
- * This transmits the NTB if there are frames waiting.
|
||||
+ * The transmit should only be run if no skb data has been sent
|
||||
+ * for a certain duration.
|
||||
*/
|
||||
-static void ncm_tx_tasklet(unsigned long data)
|
||||
+static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
|
||||
{
|
||||
- struct f_ncm *ncm = (void *)data;
|
||||
-
|
||||
- if (ncm->timer_stopping)
|
||||
- return;
|
||||
+ struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
|
||||
|
||||
/* Only send if data is available. */
|
||||
- if (ncm->skb_tx_data) {
|
||||
+ if (!ncm->timer_stopping && ncm->skb_tx_data) {
|
||||
ncm->timer_force_tx = true;
|
||||
|
||||
/* XXX This allowance of a NULL skb argument to ndo_start_xmit
|
||||
@@ -1175,16 +1171,6 @@ static void ncm_tx_tasklet(unsigned long data)
|
||||
|
||||
ncm->timer_force_tx = false;
|
||||
}
|
||||
-}
|
||||
-
|
||||
-/*
|
||||
- * The transmit should only be run if no skb data has been sent
|
||||
- * for a certain duration.
|
||||
- */
|
||||
-static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
|
||||
-{
|
||||
- struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
|
||||
- tasklet_schedule(&ncm->tx_tasklet);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
@@ -1517,8 +1503,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
ncm->port.open = ncm_open;
|
||||
ncm->port.close = ncm_close;
|
||||
|
||||
- tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
|
||||
- hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
+ hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
ncm->task_timer.function = ncm_tx_timeout;
|
||||
|
||||
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
|
||||
@@ -1627,7 +1612,6 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
DBG(c->cdev, "ncm unbind\n");
|
||||
|
||||
hrtimer_cancel(&ncm->task_timer);
|
||||
- tasklet_kill(&ncm->tx_tasklet);
|
||||
|
||||
ncm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
From 5f46919e7892edef70b9289e2c8418e4f9ffe804 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Thu, 23 Nov 2017 16:39:17 +0100
|
||||
Subject: [PATCH 050/450] net/mvpp2: Replace tasklet with softirq hrtimer
|
||||
|
||||
The tx_done_tasklet tasklet is used in invoke the hrtimer
|
||||
(mvpp2_hr_timer_cb) in softirq context. This can be also achieved without
|
||||
the tasklet but with HRTIMER_MODE_SOFT as hrtimer mode.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Signed-off-by: Anna-Maria Gleixner <anna-maria@linutronix.de>
|
||||
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
Cc: netdev@vger.kernel.org
|
||||
Cc: "David S. Miller" <davem@davemloft.net>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/net/ethernet/marvell/mvpp2.c | 62 +++++++++++-----------------
|
||||
1 file changed, 25 insertions(+), 37 deletions(-)
|
||||
|
||||
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
|
||||
index 00e6f1d155a6..9c69ab2c5b07 100644
|
||||
--- a/drivers/net/ethernet/marvell/mvpp2.c
|
||||
+++ b/drivers/net/ethernet/marvell/mvpp2.c
|
||||
@@ -831,9 +831,8 @@ struct mvpp2_pcpu_stats {
|
||||
/* Per-CPU port control */
|
||||
struct mvpp2_port_pcpu {
|
||||
struct hrtimer tx_done_timer;
|
||||
+ struct net_device *dev;
|
||||
bool timer_scheduled;
|
||||
- /* Tasklet for egress finalization */
|
||||
- struct tasklet_struct tx_done_tasklet;
|
||||
};
|
||||
|
||||
struct mvpp2_queue_vector {
|
||||
@@ -5955,46 +5954,34 @@ static void mvpp2_link_event(struct net_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
-static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu)
|
||||
-{
|
||||
- ktime_t interval;
|
||||
-
|
||||
- if (!port_pcpu->timer_scheduled) {
|
||||
- port_pcpu->timer_scheduled = true;
|
||||
- interval = MVPP2_TXDONE_HRTIMER_PERIOD_NS;
|
||||
- hrtimer_start(&port_pcpu->tx_done_timer, interval,
|
||||
- HRTIMER_MODE_REL_PINNED);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void mvpp2_tx_proc_cb(unsigned long data)
|
||||
+static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer)
|
||||
{
|
||||
- struct net_device *dev = (struct net_device *)data;
|
||||
- struct mvpp2_port *port = netdev_priv(dev);
|
||||
- struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu);
|
||||
+ struct net_device *dev;
|
||||
+ struct mvpp2_port *port;
|
||||
+ struct mvpp2_port_pcpu *port_pcpu;
|
||||
unsigned int tx_todo, cause;
|
||||
|
||||
+ port_pcpu = container_of(timer, struct mvpp2_port_pcpu, tx_done_timer);
|
||||
+ dev = port_pcpu->dev;
|
||||
+
|
||||
if (!netif_running(dev))
|
||||
- return;
|
||||
+ return HRTIMER_NORESTART;
|
||||
+
|
||||
port_pcpu->timer_scheduled = false;
|
||||
+ port = netdev_priv(dev);
|
||||
|
||||
/* Process all the Tx queues */
|
||||
cause = (1 << port->ntxqs) - 1;
|
||||
tx_todo = mvpp2_tx_done(port, cause, smp_processor_id());
|
||||
|
||||
/* Set the timer in case not all the packets were processed */
|
||||
- if (tx_todo)
|
||||
- mvpp2_timer_set(port_pcpu);
|
||||
-}
|
||||
-
|
||||
-static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer)
|
||||
-{
|
||||
- struct mvpp2_port_pcpu *port_pcpu = container_of(timer,
|
||||
- struct mvpp2_port_pcpu,
|
||||
- tx_done_timer);
|
||||
-
|
||||
- tasklet_schedule(&port_pcpu->tx_done_tasklet);
|
||||
+ if (tx_todo && !port_pcpu->timer_scheduled) {
|
||||
+ port_pcpu->timer_scheduled = true;
|
||||
+ hrtimer_forward_now(&port_pcpu->tx_done_timer,
|
||||
+ MVPP2_TXDONE_HRTIMER_PERIOD_NS);
|
||||
|
||||
+ return HRTIMER_RESTART;
|
||||
+ }
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
@@ -6484,7 +6471,12 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
txq_pcpu->count > 0) {
|
||||
struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu);
|
||||
|
||||
- mvpp2_timer_set(port_pcpu);
|
||||
+ if (!port_pcpu->timer_scheduled) {
|
||||
+ port_pcpu->timer_scheduled = true;
|
||||
+ hrtimer_start(&port_pcpu->tx_done_timer,
|
||||
+ MVPP2_TXDONE_HRTIMER_PERIOD_NS,
|
||||
+ HRTIMER_MODE_REL_PINNED_SOFT);
|
||||
+ }
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@@ -6875,7 +6867,6 @@ static int mvpp2_stop(struct net_device *dev)
|
||||
|
||||
hrtimer_cancel(&port_pcpu->tx_done_timer);
|
||||
port_pcpu->timer_scheduled = false;
|
||||
- tasklet_kill(&port_pcpu->tx_done_tasklet);
|
||||
}
|
||||
}
|
||||
mvpp2_cleanup_rxqs(port);
|
||||
@@ -7648,13 +7639,10 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
||||
port_pcpu = per_cpu_ptr(port->pcpu, cpu);
|
||||
|
||||
hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC,
|
||||
- HRTIMER_MODE_REL_PINNED);
|
||||
+ HRTIMER_MODE_REL_PINNED_SOFT);
|
||||
port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb;
|
||||
port_pcpu->timer_scheduled = false;
|
||||
-
|
||||
- tasklet_init(&port_pcpu->tx_done_tasklet,
|
||||
- mvpp2_tx_proc_cb,
|
||||
- (unsigned long)dev);
|
||||
+ port_pcpu->dev = dev;
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
From 507ef937323c7aeb59dad74c200aaaa9f16e6150 Mon Sep 17 00:00:00 2001
|
||||
From: Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
Date: Fri, 11 Sep 2015 21:21:23 +0300
|
||||
Subject: [PATCH 052/450] ARM: smp: Move clear_tasks_mm_cpumask() call to
|
||||
__cpu_die()
|
||||
|
||||
When running with the RT-kernel (4.1.5-rt5) on TI OMAP dra7-evm and trying
|
||||
to do Suspend to RAM, the following backtrace occurs:
|
||||
|
||||
Disabling non-boot CPUs ...
|
||||
PM: noirq suspend of devices complete after 7.295 msecs
|
||||
Disabling non-boot CPUs ...
|
||||
BUG: sleeping function called from invalid context at kernel/locking/rtmutex.c:917
|
||||
in_atomic(): 1, irqs_disabled(): 128, pid: 18, name: migration/1
|
||||
INFO: lockdep is turned off.
|
||||
irq event stamp: 122
|
||||
hardirqs last enabled at (121): [<c06ac0ac>] _raw_spin_unlock_irqrestore+0x88/0x90
|
||||
hardirqs last disabled at (122): [<c06abed0>] _raw_spin_lock_irq+0x28/0x5c
|
||||
softirqs last enabled at (0): [<c003d294>] copy_process.part.52+0x410/0x19d8
|
||||
softirqs last disabled at (0): [< (null)>] (null)
|
||||
Preemption disabled at:[< (null)>] (null)
|
||||
CPU: 1 PID: 18 Comm: migration/1 Tainted: G W 4.1.4-rt3-01046-g96ac8da #204
|
||||
Hardware name: Generic DRA74X (Flattened Device Tree)
|
||||
[<c0019134>] (unwind_backtrace) from [<c0014774>] (show_stack+0x20/0x24)
|
||||
[<c0014774>] (show_stack) from [<c06a70f4>] (dump_stack+0x88/0xdc)
|
||||
[<c06a70f4>] (dump_stack) from [<c006cab8>] (___might_sleep+0x198/0x2a8)
|
||||
[<c006cab8>] (___might_sleep) from [<c06ac4dc>] (rt_spin_lock+0x30/0x70)
|
||||
[<c06ac4dc>] (rt_spin_lock) from [<c013f790>] (find_lock_task_mm+0x9c/0x174)
|
||||
[<c013f790>] (find_lock_task_mm) from [<c00409ac>] (clear_tasks_mm_cpumask+0xb4/0x1ac)
|
||||
[<c00409ac>] (clear_tasks_mm_cpumask) from [<c00166a4>] (__cpu_disable+0x98/0xbc)
|
||||
[<c00166a4>] (__cpu_disable) from [<c06a2e8c>] (take_cpu_down+0x1c/0x50)
|
||||
[<c06a2e8c>] (take_cpu_down) from [<c00f2600>] (multi_cpu_stop+0x11c/0x158)
|
||||
[<c00f2600>] (multi_cpu_stop) from [<c00f2a9c>] (cpu_stopper_thread+0xc4/0x184)
|
||||
[<c00f2a9c>] (cpu_stopper_thread) from [<c0069058>] (smpboot_thread_fn+0x18c/0x324)
|
||||
[<c0069058>] (smpboot_thread_fn) from [<c00649c4>] (kthread+0xe8/0x104)
|
||||
[<c00649c4>] (kthread) from [<c0010058>] (ret_from_fork+0x14/0x3c)
|
||||
CPU1: shutdown
|
||||
PM: Calling sched_clock_suspend+0x0/0x40
|
||||
PM: Calling timekeeping_suspend+0x0/0x2e0
|
||||
PM: Calling irq_gc_suspend+0x0/0x68
|
||||
PM: Calling fw_suspend+0x0/0x2c
|
||||
PM: Calling cpu_pm_suspend+0x0/0x28
|
||||
|
||||
Also, sometimes system stucks right after displaying "Disabling non-boot
|
||||
CPUs ...". The root cause of above backtrace is task_lock() which takes
|
||||
a sleeping lock on -RT.
|
||||
|
||||
To fix the issue, move clear_tasks_mm_cpumask() call from __cpu_disable()
|
||||
to __cpu_die() which is called on the thread which is asking for a target
|
||||
CPU to be shutdown. In addition, this change restores CPUhotplug functionality
|
||||
on TI OMAP dra7-evm and CPU1 can be unplugged/plugged many times.
|
||||
|
||||
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
|
||||
Cc: Steven Rostedt <rostedt@goodmis.org>
|
||||
Cc: <linux-arm-kernel@lists.infradead.org>
|
||||
Cc: Sekhar Nori <nsekhar@ti.com>
|
||||
Cc: Austin Schuh <austin@peloton-tech.com>
|
||||
Cc: <philipp@peloton-tech.com>
|
||||
Cc: Russell King <linux@arm.linux.org.uk>
|
||||
Cc: <bigeasy@linutronix.de>
|
||||
Cc: stable-rt@vger.kernel.org
|
||||
Link: http://lkml.kernel.org/r/1441995683-30817-1-git-send-email-grygorii.strashko@ti.com
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
---
|
||||
arch/arm/kernel/smp.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
|
||||
index e61af0600133..d8f2e77d5651 100644
|
||||
--- a/arch/arm/kernel/smp.c
|
||||
+++ b/arch/arm/kernel/smp.c
|
||||
@@ -237,8 +237,6 @@ int __cpu_disable(void)
|
||||
flush_cache_louis();
|
||||
local_flush_tlb_all();
|
||||
|
||||
- clear_tasks_mm_cpumask(cpu);
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -256,6 +254,7 @@ void __cpu_die(unsigned int cpu)
|
||||
}
|
||||
pr_debug("CPU%u: shutdown\n", cpu);
|
||||
|
||||
+ clear_tasks_mm_cpumask(cpu);
|
||||
/*
|
||||
* platform_cpu_kill() is generally expected to do the powering off
|
||||
* and/or cutting of clocks to the dying CPU. Optionally, this may
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
From 04d4c6f31743974003d211da27cfe093f8cb724d Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Fri, 6 Nov 2015 18:51:03 +0100
|
||||
Subject: [PATCH 053/450] rtmutex: Handle non enqueued waiters gracefully
|
||||
|
||||
Yimin debugged that in case of a PI wakeup in progress when
|
||||
rt_mutex_start_proxy_lock() calls task_blocks_on_rt_mutex() the latter
|
||||
returns -EAGAIN and in consequence the remove_waiter() call runs into
|
||||
a BUG_ON() because there is nothing to remove.
|
||||
|
||||
Guard it with rt_mutex_has_waiters(). This is a quick fix which is
|
||||
easy to backport. The proper fix is to have a central check in
|
||||
remove_waiter() so we can call it unconditionally.
|
||||
|
||||
Reported-and-debugged-by: Yimin Deng <yimin11.deng@gmail.com>
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Cc: stable-rt@vger.kernel.org
|
||||
---
|
||||
kernel/locking/rtmutex.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
|
||||
index 456750bf9c95..a8119deef92e 100644
|
||||
--- a/kernel/locking/rtmutex.c
|
||||
+++ b/kernel/locking/rtmutex.c
|
||||
@@ -1750,7 +1750,7 @@ int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
- if (unlikely(ret))
|
||||
+ if (ret && rt_mutex_has_waiters(lock))
|
||||
remove_waiter(lock, waiter);
|
||||
|
||||
debug_rt_mutex_print_deadlock(waiter);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
From 3152ba2bf24f52a147556852a29522b67cb6ceb0 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Wed, 14 Sep 2016 11:52:17 +0200
|
||||
Subject: [PATCH 054/450] rbtree: include rcu.h because we use it
|
||||
|
||||
Since commit c1adf20052d8 ("Introduce rb_replace_node_rcu()")
|
||||
rbtree_augmented.h uses RCU related data structures but does not include
|
||||
them. It works as long as gets somehow included before that and fails
|
||||
otherwise.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/rbtree_augmented.h | 1 +
|
||||
include/linux/rbtree_latch.h | 1 +
|
||||
2 files changed, 2 insertions(+)
|
||||
|
||||
diff --git a/include/linux/rbtree_augmented.h b/include/linux/rbtree_augmented.h
|
||||
index 6bfd2b581f75..af8a61be2d8d 100644
|
||||
--- a/include/linux/rbtree_augmented.h
|
||||
+++ b/include/linux/rbtree_augmented.h
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/rbtree.h>
|
||||
+#include <linux/rcupdate.h>
|
||||
|
||||
/*
|
||||
* Please note - only struct rb_augment_callbacks and the prototypes for
|
||||
diff --git a/include/linux/rbtree_latch.h b/include/linux/rbtree_latch.h
|
||||
index ece43e882b56..7d012faa509a 100644
|
||||
--- a/include/linux/rbtree_latch.h
|
||||
+++ b/include/linux/rbtree_latch.h
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/seqlock.h>
|
||||
+#include <linux/rcupdate.h>
|
||||
|
||||
struct latch_tree_node {
|
||||
struct rb_node node[2];
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
From 0327ee53081aa5cda616879003e4817ee6d231d3 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Fri, 21 Oct 2016 10:54:50 +0200
|
||||
Subject: [PATCH 055/450] rxrpc: remove unused static variables
|
||||
|
||||
The rxrpc_security_methods and rxrpc_security_sem user has been removed
|
||||
in 648af7fca159 ("rxrpc: Absorb the rxkad security module"). This was
|
||||
noticed by kbuild test robot for the -RT tree but is also true for !RT.
|
||||
|
||||
Reported-by: kbuild test robot <fengguang.wu@intel.com>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
net/rxrpc/security.c | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
|
||||
index e9f428351293..c4479afe8ae7 100644
|
||||
--- a/net/rxrpc/security.c
|
||||
+++ b/net/rxrpc/security.c
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <keys/rxrpc-type.h>
|
||||
#include "ar-internal.h"
|
||||
|
||||
-static LIST_HEAD(rxrpc_security_methods);
|
||||
-static DECLARE_RWSEM(rxrpc_security_sem);
|
||||
-
|
||||
static const struct rxrpc_security *rxrpc_security_types[] = {
|
||||
[RXRPC_SECURITY_NONE] = &rxrpc_no_security,
|
||||
#ifdef CONFIG_RXKAD
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
From fa6ce1fa81dfde47d57ea111931de106e2977ebc Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Wed, 4 Oct 2017 09:55:58 +0200
|
||||
Subject: [PATCH 056/450] mfd: syscon: atmel-smc: include string.h
|
||||
|
||||
The string.h header file is needed for the memset() definition. The RT
|
||||
build fails because it is not pulled in via other header files.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/mfd/atmel-smc.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
|
||||
index 7d77948567d7..0adbd2e796fe 100644
|
||||
--- a/drivers/mfd/atmel-smc.c
|
||||
+++ b/drivers/mfd/atmel-smc.c
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon/atmel-smc.h>
|
||||
+#include <linux/string.h>
|
||||
|
||||
/**
|
||||
* atmel_smc_cs_conf_init - initialize a SMC CS conf
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
From 307536cbf7b07eeaf5582bf169c3a2b235720280 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Mon, 4 Dec 2017 13:11:10 +0100
|
||||
Subject: [PATCH 057/450] sched/swait: include wait.h
|
||||
|
||||
kbuild bot reported against an intermediate RT patch that the build
|
||||
fails with:
|
||||
|
||||
> In file included from include/linux/completion.h:12:0,
|
||||
> from include/linux/rcupdate_wait.h:10,
|
||||
> from kernel/rcu/srcutiny.c:27:
|
||||
> kernel/rcu/srcutiny.c: In function 'srcu_drive_gp':
|
||||
> >> include/linux/swait.h:172:7: error: implicit declaration of function '___wait_is_interruptible'; did you mean '__swait_event_interruptible'?
|
||||
> if (___wait_is_interruptible(state) && __int) { \
|
||||
|
||||
That error vanishes a few patches later (in the RT queue) because wait.h
|
||||
is then pulled in by other means. It does not seem to surface on !RT.
|
||||
I think that swait should include a header file for a function/macro
|
||||
(___wait_is_interruptible()) it is using.
|
||||
|
||||
Reported-by: kbuild test robot <fengguang.wu@intel.com>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/swait.h | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/include/linux/swait.h b/include/linux/swait.h
|
||||
index c98aaf677466..84f9745365ff 100644
|
||||
--- a/include/linux/swait.h
|
||||
+++ b/include/linux/swait.h
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/spinlock.h>
|
||||
+#include <linux/wait.h>
|
||||
#include <asm/current.h>
|
||||
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
From 993ed674f91546159ccd72fcfccb05b7179df734 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 21 Sep 2017 15:35:57 +0200
|
||||
Subject: [PATCH 059/450] Bluetooth: avoid recursive locking in
|
||||
hci_send_to_channel()
|
||||
|
||||
Mart reported a deadlock in -RT in the call path:
|
||||
hci_send_monitor_ctrl_event() -> hci_send_to_channel()
|
||||
|
||||
because both functions acquire the same read lock hci_sk_list.lock. This
|
||||
is also a mainline issue because the qrwlock implementation is writer
|
||||
fair (the traditional rwlock implementation is reader biased).
|
||||
|
||||
To avoid the deadlock there is now __hci_send_to_channel() which expects
|
||||
the readlock to be held.
|
||||
|
||||
Cc: Marcel Holtmann <marcel@holtmann.org>
|
||||
Cc: Johan Hedberg <johan.hedberg@intel.com>
|
||||
Cc: stable-rt@vger.kernel.org
|
||||
Fixes: 38ceaa00d02d ("Bluetooth: Add support for sending MGMT commands and events to monitor")
|
||||
Reported-by: Mart van de Wege <mvdwege@gmail.com>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
net/bluetooth/hci_sock.c | 17 +++++++++++------
|
||||
1 file changed, 11 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
|
||||
index 65d734c165bd..923e9a271872 100644
|
||||
--- a/net/bluetooth/hci_sock.c
|
||||
+++ b/net/bluetooth/hci_sock.c
|
||||
@@ -251,15 +251,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
}
|
||||
|
||||
/* Send frame to sockets with specific channel */
|
||||
-void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
- int flag, struct sock *skip_sk)
|
||||
+static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
+ int flag, struct sock *skip_sk)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
BT_DBG("channel %u len %d", channel, skb->len);
|
||||
|
||||
- read_lock(&hci_sk_list.lock);
|
||||
-
|
||||
sk_for_each(sk, &hci_sk_list.head) {
|
||||
struct sk_buff *nskb;
|
||||
|
||||
@@ -285,6 +283,13 @@ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
kfree_skb(nskb);
|
||||
}
|
||||
|
||||
+}
|
||||
+
|
||||
+void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
|
||||
+ int flag, struct sock *skip_sk)
|
||||
+{
|
||||
+ read_lock(&hci_sk_list.lock);
|
||||
+ __hci_send_to_channel(channel, skb, flag, skip_sk);
|
||||
read_unlock(&hci_sk_list.lock);
|
||||
}
|
||||
|
||||
@@ -388,8 +393,8 @@ void hci_send_monitor_ctrl_event(struct hci_dev *hdev, u16 event,
|
||||
hdr->index = index;
|
||||
hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
|
||||
|
||||
- hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
|
||||
- HCI_SOCK_TRUSTED, NULL);
|
||||
+ __hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
|
||||
+ HCI_SOCK_TRUSTED, NULL);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
From fdbd8254ab62179f7aee6033e4ff8353b4ade395 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 21 Sep 2017 17:21:40 +0200
|
||||
Subject: [PATCH 060/450] iommu/iova: Use raw_cpu_ptr() instead of
|
||||
get_cpu_ptr() for ->fq
|
||||
|
||||
get_cpu_ptr() disabled preemption and returns the ->fq object of the
|
||||
current CPU. raw_cpu_ptr() does the same except that it not disable
|
||||
preemption which means the scheduler can move it to another CPU after it
|
||||
obtained the per-CPU object.
|
||||
In this case this is not bad because the data structure itself is
|
||||
protected with a spin_lock. This change shouldn't matter however on RT
|
||||
it does because the sleeping lock can't be accessed with disabled
|
||||
preemption.
|
||||
|
||||
Cc: Joerg Roedel <joro@8bytes.org>
|
||||
Cc: iommu@lists.linux-foundation.org
|
||||
Reported-by: vinadhy@gmail.com
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/iommu/iova.c | 4 +---
|
||||
1 file changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
|
||||
index 33edfa794ae9..b30900025c62 100644
|
||||
--- a/drivers/iommu/iova.c
|
||||
+++ b/drivers/iommu/iova.c
|
||||
@@ -570,7 +570,7 @@ void queue_iova(struct iova_domain *iovad,
|
||||
unsigned long pfn, unsigned long pages,
|
||||
unsigned long data)
|
||||
{
|
||||
- struct iova_fq *fq = get_cpu_ptr(iovad->fq);
|
||||
+ struct iova_fq *fq = raw_cpu_ptr(iovad->fq);
|
||||
unsigned long flags;
|
||||
unsigned idx;
|
||||
|
||||
@@ -600,8 +600,6 @@ void queue_iova(struct iova_domain *iovad,
|
||||
if (atomic_cmpxchg(&iovad->fq_timer_on, 0, 1) == 0)
|
||||
mod_timer(&iovad->fq_timer,
|
||||
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
|
||||
-
|
||||
- put_cpu_ptr(iovad->fq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(queue_iova);
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
From fbcbb30ee0869c20e0058364661c2bb612c48bbf Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 5 Oct 2017 14:38:52 +0200
|
||||
Subject: [PATCH 061/450] greybus: audio: don't inclide rwlock.h directly.
|
||||
|
||||
rwlock.h should not be included directly. Instead linux/splinlock.h
|
||||
should be included. One thing it does is to break the RT build.
|
||||
|
||||
Cc: Vaibhav Agarwal <vaibhav.sr@gmail.com>
|
||||
Cc: Mark Greer <mgreer@animalcreek.com>
|
||||
Cc: Johan Hovold <johan@kernel.org>
|
||||
Cc: Alex Elder <elder@kernel.org>
|
||||
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Cc: greybus-dev@lists.linaro.org
|
||||
Cc: devel@driverdev.osuosl.org
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/staging/greybus/audio_manager.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c
|
||||
index aa6508b44fab..045696ce85c7 100644
|
||||
--- a/drivers/staging/greybus/audio_manager.c
|
||||
+++ b/drivers/staging/greybus/audio_manager.c
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
-#include <linux/rwlock.h>
|
||||
+#include <linux/spinlock.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
#include "audio_manager.h"
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
From 53e8aad4871bacbe9ca9dae684d61821f520300d Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 5 Oct 2017 14:38:52 +0200
|
||||
Subject: [PATCH 062/450] xen/9pfs: don't inclide rwlock.h directly.
|
||||
|
||||
rwlock.h should not be included directly. Instead linux/splinlock.h
|
||||
should be included. One thing it does is to break the RT build.
|
||||
|
||||
Cc: Eric Van Hensbergen <ericvh@gmail.com>
|
||||
Cc: Ron Minnich <rminnich@sandia.gov>
|
||||
Cc: Latchesar Ionkov <lucho@ionkov.net>
|
||||
Cc: "David S. Miller" <davem@davemloft.net>
|
||||
Cc: v9fs-developer@lists.sourceforge.net
|
||||
Cc: netdev@vger.kernel.org
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
net/9p/trans_xen.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c
|
||||
index c10bdf63eae7..84a49f2bcfbc 100644
|
||||
--- a/net/9p/trans_xen.c
|
||||
+++ b/net/9p/trans_xen.c
|
||||
@@ -38,7 +38,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
-#include <linux/rwlock.h>
|
||||
#include <net/9p/9p.h>
|
||||
#include <net/9p/client.h>
|
||||
#include <net/9p/transport.h>
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
From 0d3d9c5f072e910b2f184bfac6c15b9690a779e9 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Thu, 30 Nov 2017 16:06:13 +0100
|
||||
Subject: [PATCH 063/450] drm/i915: properly init lockdep class
|
||||
|
||||
The code has an ifdef and uses two functions to either init the bare
|
||||
spinlock or init it and set a lock-class. It is possible to do the same
|
||||
thing without an ifdef.
|
||||
With this patch (in debug case) we first use the "default" lock class
|
||||
which is later overwritten to the supplied one. Without lockdep the set
|
||||
name/class function vanishes.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/gpu/drm/i915/i915_gem_timeline.c | 5 +----
|
||||
1 file changed, 1 insertion(+), 4 deletions(-)
|
||||
|
||||
diff --git a/drivers/gpu/drm/i915/i915_gem_timeline.c b/drivers/gpu/drm/i915/i915_gem_timeline.c
|
||||
index c597ce277a04..c1108d3921f8 100644
|
||||
--- a/drivers/gpu/drm/i915/i915_gem_timeline.c
|
||||
+++ b/drivers/gpu/drm/i915/i915_gem_timeline.c
|
||||
@@ -33,11 +33,8 @@ static void __intel_timeline_init(struct intel_timeline *tl,
|
||||
{
|
||||
tl->fence_context = context;
|
||||
tl->common = parent;
|
||||
-#ifdef CONFIG_DEBUG_SPINLOCK
|
||||
- __raw_spin_lock_init(&tl->lock.rlock, lockname, lockclass);
|
||||
-#else
|
||||
spin_lock_init(&tl->lock);
|
||||
-#endif
|
||||
+ lockdep_set_class_and_name(&tl->lock, lockclass, lockname);
|
||||
init_request_active(&tl->last_request, NULL);
|
||||
INIT_LIST_HEAD(&tl->requests);
|
||||
i915_syncmap_init(&tl->sync);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
From 74826a26ced7740701c5d18e2678b2771ef4b9f1 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Gleixner <tglx@linutronix.de>
|
||||
Date: Fri, 22 Dec 2017 15:51:15 +0100
|
||||
Subject: [PATCH 064/450] timerqueue: Document return values of
|
||||
timerqueue_add/del()
|
||||
|
||||
The return values of timerqueue_add/del() are not documented in the kernel doc
|
||||
comment. Add proper documentation.
|
||||
|
||||
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
||||
Cc: rt@linutronix.de
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
lib/timerqueue.c | 8 +++++---
|
||||
1 file changed, 5 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/lib/timerqueue.c b/lib/timerqueue.c
|
||||
index 4a720ed4fdaf..0d54bcbc8170 100644
|
||||
--- a/lib/timerqueue.c
|
||||
+++ b/lib/timerqueue.c
|
||||
@@ -33,8 +33,9 @@
|
||||
* @head: head of timerqueue
|
||||
* @node: timer node to be added
|
||||
*
|
||||
- * Adds the timer node to the timerqueue, sorted by the
|
||||
- * node's expires value.
|
||||
+ * Adds the timer node to the timerqueue, sorted by the node's expires
|
||||
+ * value. Returns true if the newly added timer is the first expiring timer in
|
||||
+ * the queue.
|
||||
*/
|
||||
bool timerqueue_add(struct timerqueue_head *head, struct timerqueue_node *node)
|
||||
{
|
||||
@@ -70,7 +71,8 @@ EXPORT_SYMBOL_GPL(timerqueue_add);
|
||||
* @head: head of timerqueue
|
||||
* @node: timer node to be removed
|
||||
*
|
||||
- * Removes the timer node from the timerqueue.
|
||||
+ * Removes the timer node from the timerqueue. Returns true if the queue is
|
||||
+ * not empty after the remove.
|
||||
*/
|
||||
bool timerqueue_del(struct timerqueue_head *head, struct timerqueue_node *node)
|
||||
{
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
From 5692af55627acc4af517136fd0ea965581b5bba8 Mon Sep 17 00:00:00 2001
|
||||
From: Allen Pais <allen.pais@oracle.com>
|
||||
Date: Fri, 13 Dec 2013 09:44:41 +0530
|
||||
Subject: [PATCH 065/450] sparc64: use generic rwsem spinlocks rt
|
||||
|
||||
Signed-off-by: Allen Pais <allen.pais@oracle.com>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
arch/sparc/Kconfig | 6 ++----
|
||||
1 file changed, 2 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
|
||||
index 4e83f950713e..7f9d71523763 100644
|
||||
--- a/arch/sparc/Kconfig
|
||||
+++ b/arch/sparc/Kconfig
|
||||
@@ -206,12 +206,10 @@ config NR_CPUS
|
||||
source kernel/Kconfig.hz
|
||||
|
||||
config RWSEM_GENERIC_SPINLOCK
|
||||
- bool
|
||||
- default y if SPARC32
|
||||
+ def_bool PREEMPT_RT_FULL
|
||||
|
||||
config RWSEM_XCHGADD_ALGORITHM
|
||||
- bool
|
||||
- default y if SPARC64
|
||||
+ def_bool !RWSEM_GENERIC_SPINLOCK && !PREEMPT_RT_FULL
|
||||
|
||||
config GENERIC_HWEIGHT
|
||||
bool
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
From 04c244882b9bf3a257292ac6b3cfba56121b3628 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
Date: Tue, 19 Mar 2013 14:44:30 +0100
|
||||
Subject: [PATCH 066/450] kernel/SRCU: provide a static initializer
|
||||
|
||||
There are macros for static initializer for the three out of four
|
||||
possible notifier types, that are:
|
||||
ATOMIC_NOTIFIER_HEAD()
|
||||
BLOCKING_NOTIFIER_HEAD()
|
||||
RAW_NOTIFIER_HEAD()
|
||||
|
||||
This patch provides a static initilizer for the forth type to make it
|
||||
complete.
|
||||
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/notifier.h | 42 +++++++++++++++++++++++++++++++---------
|
||||
include/linux/srcutiny.h | 6 +++---
|
||||
include/linux/srcutree.h | 6 +++---
|
||||
3 files changed, 39 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/include/linux/notifier.h b/include/linux/notifier.h
|
||||
index 6d731110e0db..e758627da14d 100644
|
||||
--- a/include/linux/notifier.h
|
||||
+++ b/include/linux/notifier.h
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
* Alan Cox <Alan.Cox@linux.org>
|
||||
*/
|
||||
-
|
||||
+
|
||||
#ifndef _LINUX_NOTIFIER_H
|
||||
#define _LINUX_NOTIFIER_H
|
||||
#include <linux/errno.h>
|
||||
@@ -43,9 +43,7 @@
|
||||
* in srcu_notifier_call_chain(): no cache bounces and no memory barriers.
|
||||
* As compensation, srcu_notifier_chain_unregister() is rather expensive.
|
||||
* SRCU notifier chains should be used when the chain will be called very
|
||||
- * often but notifier_blocks will seldom be removed. Also, SRCU notifier
|
||||
- * chains are slightly more difficult to use because they require special
|
||||
- * runtime initialization.
|
||||
+ * often but notifier_blocks will seldom be removed.
|
||||
*/
|
||||
|
||||
struct notifier_block;
|
||||
@@ -91,7 +89,7 @@ struct srcu_notifier_head {
|
||||
(name)->head = NULL; \
|
||||
} while (0)
|
||||
|
||||
-/* srcu_notifier_heads must be initialized and cleaned up dynamically */
|
||||
+/* srcu_notifier_heads must be cleaned up dynamically */
|
||||
extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
|
||||
#define srcu_cleanup_notifier_head(name) \
|
||||
cleanup_srcu_struct(&(name)->srcu);
|
||||
@@ -104,7 +102,13 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
|
||||
.head = NULL }
|
||||
#define RAW_NOTIFIER_INIT(name) { \
|
||||
.head = NULL }
|
||||
-/* srcu_notifier_heads cannot be initialized statically */
|
||||
+
|
||||
+#define SRCU_NOTIFIER_INIT(name, pcpu) \
|
||||
+ { \
|
||||
+ .mutex = __MUTEX_INITIALIZER(name.mutex), \
|
||||
+ .head = NULL, \
|
||||
+ .srcu = __SRCU_STRUCT_INIT(name.srcu, pcpu), \
|
||||
+ }
|
||||
|
||||
#define ATOMIC_NOTIFIER_HEAD(name) \
|
||||
struct atomic_notifier_head name = \
|
||||
@@ -116,6 +120,26 @@ extern void srcu_init_notifier_head(struct srcu_notifier_head *nh);
|
||||
struct raw_notifier_head name = \
|
||||
RAW_NOTIFIER_INIT(name)
|
||||
|
||||
+#ifdef CONFIG_TREE_SRCU
|
||||
+#define _SRCU_NOTIFIER_HEAD(name, mod) \
|
||||
+ static DEFINE_PER_CPU(struct srcu_data, \
|
||||
+ name##_head_srcu_data); \
|
||||
+ mod struct srcu_notifier_head name = \
|
||||
+ SRCU_NOTIFIER_INIT(name, name##_head_srcu_data)
|
||||
+
|
||||
+#else
|
||||
+#define _SRCU_NOTIFIER_HEAD(name, mod) \
|
||||
+ mod struct srcu_notifier_head name = \
|
||||
+ SRCU_NOTIFIER_INIT(name, name)
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+#define SRCU_NOTIFIER_HEAD(name) \
|
||||
+ _SRCU_NOTIFIER_HEAD(name, )
|
||||
+
|
||||
+#define SRCU_NOTIFIER_HEAD_STATIC(name) \
|
||||
+ _SRCU_NOTIFIER_HEAD(name, static)
|
||||
+
|
||||
#ifdef __KERNEL__
|
||||
|
||||
extern int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
|
||||
@@ -185,12 +209,12 @@ static inline int notifier_to_errno(int ret)
|
||||
|
||||
/*
|
||||
* Declared notifiers so far. I can imagine quite a few more chains
|
||||
- * over time (eg laptop power reset chains, reboot chain (to clean
|
||||
+ * over time (eg laptop power reset chains, reboot chain (to clean
|
||||
* device units up), device [un]mount chain, module load/unload chain,
|
||||
- * low memory chain, screenblank chain (for plug in modular screenblankers)
|
||||
+ * low memory chain, screenblank chain (for plug in modular screenblankers)
|
||||
* VC switch chains (for loadable kernel svgalib VC switch helpers) etc...
|
||||
*/
|
||||
-
|
||||
+
|
||||
/* CPU notfiers are defined in include/linux/cpu.h. */
|
||||
|
||||
/* netdevice notifiers are defined in include/linux/netdevice.h */
|
||||
diff --git a/include/linux/srcutiny.h b/include/linux/srcutiny.h
|
||||
index 261471f407a5..f41d2fb09f87 100644
|
||||
--- a/include/linux/srcutiny.h
|
||||
+++ b/include/linux/srcutiny.h
|
||||
@@ -43,7 +43,7 @@ struct srcu_struct {
|
||||
|
||||
void srcu_drive_gp(struct work_struct *wp);
|
||||
|
||||
-#define __SRCU_STRUCT_INIT(name) \
|
||||
+#define __SRCU_STRUCT_INIT(name, __ignored) \
|
||||
{ \
|
||||
.srcu_wq = __SWAIT_QUEUE_HEAD_INITIALIZER(name.srcu_wq), \
|
||||
.srcu_cb_tail = &name.srcu_cb_head, \
|
||||
@@ -56,9 +56,9 @@ void srcu_drive_gp(struct work_struct *wp);
|
||||
* Tree SRCU, which needs some per-CPU data.
|
||||
*/
|
||||
#define DEFINE_SRCU(name) \
|
||||
- struct srcu_struct name = __SRCU_STRUCT_INIT(name)
|
||||
+ struct srcu_struct name = __SRCU_STRUCT_INIT(name, name)
|
||||
#define DEFINE_STATIC_SRCU(name) \
|
||||
- static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
|
||||
+ static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name)
|
||||
|
||||
void synchronize_srcu(struct srcu_struct *sp);
|
||||
|
||||
diff --git a/include/linux/srcutree.h b/include/linux/srcutree.h
|
||||
index a949f4f9e4d7..85ae8c0dddb4 100644
|
||||
--- a/include/linux/srcutree.h
|
||||
+++ b/include/linux/srcutree.h
|
||||
@@ -104,9 +104,9 @@ struct srcu_struct {
|
||||
#define SRCU_STATE_SCAN1 1
|
||||
#define SRCU_STATE_SCAN2 2
|
||||
|
||||
-#define __SRCU_STRUCT_INIT(name) \
|
||||
+#define __SRCU_STRUCT_INIT(name, pcpu_name) \
|
||||
{ \
|
||||
- .sda = &name##_srcu_data, \
|
||||
+ .sda = &pcpu_name, \
|
||||
.lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock), \
|
||||
.srcu_gp_seq_needed = 0 - 1, \
|
||||
__SRCU_DEP_MAP_INIT(name) \
|
||||
@@ -133,7 +133,7 @@ struct srcu_struct {
|
||||
*/
|
||||
#define __DEFINE_SRCU(name, is_static) \
|
||||
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data);\
|
||||
- is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
|
||||
+ is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_data)
|
||||
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
|
||||
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
From 23624e1d8e58efec7e18f61451c2feb4f37238ab Mon Sep 17 00:00:00 2001
|
||||
From: "bigeasy@linutronix.de" <bigeasy@linutronix.de>
|
||||
Date: Fri, 23 Mar 2018 18:17:36 +0100
|
||||
Subject: [PATCH 067/450] target: drop spin_lock_assert() + irqs_disabled()
|
||||
combo checks
|
||||
|
||||
There are a few functions which check for if the lock is held
|
||||
(spin_lock_assert()) and the interrupts are disabled (irqs_disabled()).
|
||||
>From looking at the code, each function is static, the caller is near by
|
||||
and does spin_lock_irq|safe(). As Linus puts it:
|
||||
|
||||
|It's not like this is some function that is exported to random users,
|
||||
|and we should check that the calling convention is right.
|
||||
|
|
||||
|This looks like "it may have been useful during coding to document
|
||||
|things, but it's not useful long-term".
|
||||
|
||||
Remove those checks.
|
||||
|
||||
Reviewed-by: Bart Van Assche <bart.vanassche@wdc.com>
|
||||
Reported-by: Arnaldo Carvalho de Melo <acme@kernel.org>
|
||||
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
drivers/target/target_core_tmr.c | 2 --
|
||||
drivers/target/target_core_transport.c | 6 ------
|
||||
2 files changed, 8 deletions(-)
|
||||
|
||||
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
|
||||
index 9c7bc1ca341a..3d35dad1de2c 100644
|
||||
--- a/drivers/target/target_core_tmr.c
|
||||
+++ b/drivers/target/target_core_tmr.c
|
||||
@@ -114,8 +114,6 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
|
||||
{
|
||||
struct se_session *sess = se_cmd->se_sess;
|
||||
|
||||
- assert_spin_locked(&sess->sess_cmd_lock);
|
||||
- WARN_ON_ONCE(!irqs_disabled());
|
||||
/*
|
||||
* If command already reached CMD_T_COMPLETE state within
|
||||
* target_complete_cmd() or CMD_T_FABRIC_STOP due to shutdown,
|
||||
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
|
||||
index 0d0be7d8b9d6..f652e58e2988 100644
|
||||
--- a/drivers/target/target_core_transport.c
|
||||
+++ b/drivers/target/target_core_transport.c
|
||||
@@ -2967,9 +2967,6 @@ __transport_wait_for_tasks(struct se_cmd *cmd, bool fabric_stop,
|
||||
__acquires(&cmd->t_state_lock)
|
||||
{
|
||||
|
||||
- assert_spin_locked(&cmd->t_state_lock);
|
||||
- WARN_ON_ONCE(!irqs_disabled());
|
||||
-
|
||||
if (fabric_stop)
|
||||
cmd->transport_state |= CMD_T_FABRIC_STOP;
|
||||
|
||||
@@ -3239,9 +3236,6 @@ static int __transport_check_aborted_status(struct se_cmd *cmd, int send_status)
|
||||
{
|
||||
int ret;
|
||||
|
||||
- assert_spin_locked(&cmd->t_state_lock);
|
||||
- WARN_ON_ONCE(!irqs_disabled());
|
||||
-
|
||||
if (!(cmd->transport_state & CMD_T_ABORTED))
|
||||
return 0;
|
||||
/*
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
From 5949dd1e7ccd5b129d4f1fd30c9090bcc1ee8620 Mon Sep 17 00:00:00 2001
|
||||
From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
|
||||
Date: Thu, 21 Sep 2017 16:22:49 -0400
|
||||
Subject: [PATCH 070/450] tracing: Reverse the order of trace_types_lock and
|
||||
event_mutex
|
||||
|
||||
In order to make future changes where we need to call
|
||||
tracing_set_clock() from within an event command, the order of
|
||||
trace_types_lock and event_mutex must be reversed, as the event command
|
||||
will hold event_mutex and the trace_types_lock is taken from within
|
||||
tracing_set_clock().
|
||||
|
||||
Link: http://lkml.kernel.org/r/20170921162249.0dde3dca@gandalf.local.home
|
||||
|
||||
Requested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 12ecef0cb12102d8c034770173d2d1363cb97d52)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace.c | 5 +++++
|
||||
kernel/trace/trace_events.c | 31 +++++++++++++++----------------
|
||||
2 files changed, 20 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index e9cbb96cd99e..ee24e0bfc391 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -7684,6 +7684,7 @@ static int instance_mkdir(const char *name)
|
||||
struct trace_array *tr;
|
||||
int ret;
|
||||
|
||||
+ mutex_lock(&event_mutex);
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
ret = -EEXIST;
|
||||
@@ -7739,6 +7740,7 @@ static int instance_mkdir(const char *name)
|
||||
list_add(&tr->list, &ftrace_trace_arrays);
|
||||
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -7750,6 +7752,7 @@ static int instance_mkdir(const char *name)
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -7762,6 +7765,7 @@ static int instance_rmdir(const char *name)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
+ mutex_lock(&event_mutex);
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
ret = -ENODEV;
|
||||
@@ -7807,6 +7811,7 @@ static int instance_rmdir(const char *name)
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
|
||||
index d53268a4e167..1b87157edbff 100644
|
||||
--- a/kernel/trace/trace_events.c
|
||||
+++ b/kernel/trace/trace_events.c
|
||||
@@ -1406,8 +1406,8 @@ static int subsystem_open(struct inode *inode, struct file *filp)
|
||||
return -ENODEV;
|
||||
|
||||
/* Make sure the system still exists */
|
||||
- mutex_lock(&trace_types_lock);
|
||||
mutex_lock(&event_mutex);
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
|
||||
list_for_each_entry(dir, &tr->systems, list) {
|
||||
if (dir == inode->i_private) {
|
||||
@@ -1421,8 +1421,8 @@ static int subsystem_open(struct inode *inode, struct file *filp)
|
||||
}
|
||||
}
|
||||
exit_loop:
|
||||
- mutex_unlock(&event_mutex);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
if (!system)
|
||||
return -ENODEV;
|
||||
@@ -2308,15 +2308,15 @@ static void __add_event_to_tracers(struct trace_event_call *call);
|
||||
int trace_add_event_call(struct trace_event_call *call)
|
||||
{
|
||||
int ret;
|
||||
- mutex_lock(&trace_types_lock);
|
||||
mutex_lock(&event_mutex);
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
|
||||
ret = __register_event(call, NULL);
|
||||
if (ret >= 0)
|
||||
__add_event_to_tracers(call);
|
||||
|
||||
- mutex_unlock(&event_mutex);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2370,13 +2370,13 @@ int trace_remove_event_call(struct trace_event_call *call)
|
||||
{
|
||||
int ret;
|
||||
|
||||
- mutex_lock(&trace_types_lock);
|
||||
mutex_lock(&event_mutex);
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
down_write(&trace_event_sem);
|
||||
ret = probe_remove_event_call(call);
|
||||
up_write(&trace_event_sem);
|
||||
- mutex_unlock(&event_mutex);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -2438,8 +2438,8 @@ static int trace_module_notify(struct notifier_block *self,
|
||||
{
|
||||
struct module *mod = data;
|
||||
|
||||
- mutex_lock(&trace_types_lock);
|
||||
mutex_lock(&event_mutex);
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
trace_module_add_events(mod);
|
||||
@@ -2448,8 +2448,8 @@ static int trace_module_notify(struct notifier_block *self,
|
||||
trace_module_remove_events(mod);
|
||||
break;
|
||||
}
|
||||
- mutex_unlock(&event_mutex);
|
||||
mutex_unlock(&trace_types_lock);
|
||||
+ mutex_unlock(&event_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2964,24 +2964,24 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr)
|
||||
* creates the event hierachry in the @parent/events directory.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
+ *
|
||||
+ * Must be called with event_mutex held.
|
||||
*/
|
||||
int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
- mutex_lock(&event_mutex);
|
||||
+ lockdep_assert_held(&event_mutex);
|
||||
|
||||
ret = create_event_toplevel_files(parent, tr);
|
||||
if (ret)
|
||||
- goto out_unlock;
|
||||
+ goto out;
|
||||
|
||||
down_write(&trace_event_sem);
|
||||
__trace_add_event_dirs(tr);
|
||||
up_write(&trace_event_sem);
|
||||
|
||||
- out_unlock:
|
||||
- mutex_unlock(&event_mutex);
|
||||
-
|
||||
+ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3010,9 +3010,10 @@ early_event_add_tracer(struct dentry *parent, struct trace_array *tr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+/* Must be called with event_mutex held */
|
||||
int event_trace_del_tracer(struct trace_array *tr)
|
||||
{
|
||||
- mutex_lock(&event_mutex);
|
||||
+ lockdep_assert_held(&event_mutex);
|
||||
|
||||
/* Disable any event triggers and associated soft-disabled events */
|
||||
clear_event_triggers(tr);
|
||||
@@ -3033,8 +3034,6 @@ int event_trace_del_tracer(struct trace_array *tr)
|
||||
|
||||
tr->event_dir = NULL;
|
||||
|
||||
- mutex_unlock(&event_mutex);
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
From 2b1f41ec5ffa792926f78d9561c67ec7bfc7a215 Mon Sep 17 00:00:00 2001
|
||||
From: "Steven Rostedt (VMware)" <rostedt@goodmis.org>
|
||||
Date: Fri, 22 Sep 2017 16:59:02 -0400
|
||||
Subject: [PATCH 071/450] ring-buffer: Rewrite trace_recursive_(un)lock() to be
|
||||
simpler
|
||||
|
||||
The current method to prevent the ring buffer from entering into a recursize
|
||||
loop is to use a bitmask and set the bit that maps to the current context
|
||||
(normal, softirq, irq or NMI), and if that bit was already set, it is
|
||||
considered a recursive loop.
|
||||
|
||||
New code is being added that may require the ring buffer to be entered a
|
||||
second time in the current context. The recursive locking prevents that from
|
||||
happening. Instead of mapping a bitmask to the current context, just allow 4
|
||||
levels of nesting in the ring buffer. This matches the 4 context levels that
|
||||
it can already nest. It is highly unlikely to have more than two levels,
|
||||
thus it should be fine when we add the second entry into the ring buffer. If
|
||||
that proves to be a problem, we can always up the number to 8.
|
||||
|
||||
An added benefit is that reading preempt_count() to get the current level
|
||||
adds a very slight but noticeable overhead. This removes that need.
|
||||
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 1a149d7d3f45d311da1f63473736c05f30ae8a75)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/ring_buffer.c | 64 ++++++++++----------------------------
|
||||
1 file changed, 17 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
|
||||
index a1d5e0949dcf..ade8493cb69f 100644
|
||||
--- a/kernel/trace/ring_buffer.c
|
||||
+++ b/kernel/trace/ring_buffer.c
|
||||
@@ -2547,61 +2547,29 @@ rb_wakeups(struct ring_buffer *buffer, struct ring_buffer_per_cpu *cpu_buffer)
|
||||
* The lock and unlock are done within a preempt disable section.
|
||||
* The current_context per_cpu variable can only be modified
|
||||
* by the current task between lock and unlock. But it can
|
||||
- * be modified more than once via an interrupt. To pass this
|
||||
- * information from the lock to the unlock without having to
|
||||
- * access the 'in_interrupt()' functions again (which do show
|
||||
- * a bit of overhead in something as critical as function tracing,
|
||||
- * we use a bitmask trick.
|
||||
+ * be modified more than once via an interrupt. There are four
|
||||
+ * different contexts that we need to consider.
|
||||
*
|
||||
- * bit 0 = NMI context
|
||||
- * bit 1 = IRQ context
|
||||
- * bit 2 = SoftIRQ context
|
||||
- * bit 3 = normal context.
|
||||
+ * Normal context.
|
||||
+ * SoftIRQ context
|
||||
+ * IRQ context
|
||||
+ * NMI context
|
||||
*
|
||||
- * This works because this is the order of contexts that can
|
||||
- * preempt other contexts. A SoftIRQ never preempts an IRQ
|
||||
- * context.
|
||||
- *
|
||||
- * When the context is determined, the corresponding bit is
|
||||
- * checked and set (if it was set, then a recursion of that context
|
||||
- * happened).
|
||||
- *
|
||||
- * On unlock, we need to clear this bit. To do so, just subtract
|
||||
- * 1 from the current_context and AND it to itself.
|
||||
- *
|
||||
- * (binary)
|
||||
- * 101 - 1 = 100
|
||||
- * 101 & 100 = 100 (clearing bit zero)
|
||||
- *
|
||||
- * 1010 - 1 = 1001
|
||||
- * 1010 & 1001 = 1000 (clearing bit 1)
|
||||
- *
|
||||
- * The least significant bit can be cleared this way, and it
|
||||
- * just so happens that it is the same bit corresponding to
|
||||
- * the current context.
|
||||
+ * If for some reason the ring buffer starts to recurse, we
|
||||
+ * only allow that to happen at most 4 times (one for each
|
||||
+ * context). If it happens 5 times, then we consider this a
|
||||
+ * recusive loop and do not let it go further.
|
||||
*/
|
||||
|
||||
static __always_inline int
|
||||
trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
{
|
||||
- unsigned int val = cpu_buffer->current_context;
|
||||
- int bit;
|
||||
-
|
||||
- if (in_interrupt()) {
|
||||
- if (in_nmi())
|
||||
- bit = RB_CTX_NMI;
|
||||
- else if (in_irq())
|
||||
- bit = RB_CTX_IRQ;
|
||||
- else
|
||||
- bit = RB_CTX_SOFTIRQ;
|
||||
- } else
|
||||
- bit = RB_CTX_NORMAL;
|
||||
-
|
||||
- if (unlikely(val & (1 << bit)))
|
||||
+ if (cpu_buffer->current_context >= 4)
|
||||
return 1;
|
||||
|
||||
- val |= (1 << bit);
|
||||
- cpu_buffer->current_context = val;
|
||||
+ cpu_buffer->current_context++;
|
||||
+ /* Interrupts must see this update */
|
||||
+ barrier();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2609,7 +2577,9 @@ trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
static __always_inline void
|
||||
trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer)
|
||||
{
|
||||
- cpu_buffer->current_context &= cpu_buffer->current_context - 1;
|
||||
+ /* Don't let the dec leak out */
|
||||
+ barrier();
|
||||
+ cpu_buffer->current_context--;
|
||||
}
|
||||
|
||||
/**
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
From 6c8db16c6d06bad6ad65df740972283acaf82297 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:18 -0500
|
||||
Subject: [PATCH 072/450] tracing: Remove lookups from tracing_map hitcount
|
||||
|
||||
Lookups inflate the hitcount, making it essentially useless. Only
|
||||
inserts and updates should really affect the hitcount anyway, so
|
||||
explicitly filter lookups out.
|
||||
|
||||
Link: http://lkml.kernel.org/r/c8d9dc39d269a8abf88bf4102d0dfc65deb0fc7f.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 83c07ecc4203728e85fc4a2ce6fdf25d16ea118e)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/tracing_map.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
|
||||
index 305039b122fa..07e75344725b 100644
|
||||
--- a/kernel/trace/tracing_map.c
|
||||
+++ b/kernel/trace/tracing_map.c
|
||||
@@ -428,7 +428,8 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
|
||||
|
||||
if (test_key && test_key == key_hash && entry->val &&
|
||||
keys_match(key, entry->val->key, map->key_size)) {
|
||||
- atomic64_inc(&map->hits);
|
||||
+ if (!lookup_only)
|
||||
+ atomic64_inc(&map->hits);
|
||||
return entry->val;
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
From 45d6041465d780691c7c498eb7f7458bb5192ded Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:19 -0500
|
||||
Subject: [PATCH 073/450] tracing: Increase tracing map KEYS_MAX size
|
||||
|
||||
The current default for the number of subkeys in a compound key is 2,
|
||||
which is too restrictive. Increase it to a more realistic value of 3.
|
||||
|
||||
Link: http://lkml.kernel.org/r/b6952cca06d1f912eba33804a6fd6069b3847d44.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 4f36c2d85cedea60ad424d44534121ab0458069e)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/tracing_map.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/trace/tracing_map.h b/kernel/trace/tracing_map.h
|
||||
index ab0ca77331d0..5b5bbf8ae550 100644
|
||||
--- a/kernel/trace/tracing_map.h
|
||||
+++ b/kernel/trace/tracing_map.h
|
||||
@@ -6,7 +6,7 @@
|
||||
#define TRACING_MAP_BITS_MAX 17
|
||||
#define TRACING_MAP_BITS_MIN 7
|
||||
|
||||
-#define TRACING_MAP_KEYS_MAX 2
|
||||
+#define TRACING_MAP_KEYS_MAX 3
|
||||
#define TRACING_MAP_VALS_MAX 3
|
||||
#define TRACING_MAP_FIELDS_MAX (TRACING_MAP_KEYS_MAX + \
|
||||
TRACING_MAP_VALS_MAX)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,337 +0,0 @@
|
||||
From f8a450144ee205d29baae926ec23478a12f25476 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:20 -0500
|
||||
Subject: [PATCH 074/450] tracing: Make traceprobe parsing code reusable
|
||||
|
||||
traceprobe_probes_write() and traceprobe_command() actually contain
|
||||
nothing that ties them to kprobes - the code is generically useful for
|
||||
similar types of parsing elsewhere, so separate it out and move it to
|
||||
trace.c/trace.h.
|
||||
|
||||
Other than moving it, the only change is in naming:
|
||||
traceprobe_probes_write() becomes trace_parse_run_command() and
|
||||
traceprobe_command() becomes trace_run_command().
|
||||
|
||||
Link: http://lkml.kernel.org/r/ae5c26ea40c196a8986854d921eb6e713ede7e3f.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 7e465baa80293ed5f87fdf6405391d6f02110d4e)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace.c | 86 +++++++++++++++++++++++++++++++++++++
|
||||
kernel/trace/trace.h | 7 +++
|
||||
kernel/trace/trace_kprobe.c | 18 ++++----
|
||||
kernel/trace/trace_probe.c | 86 -------------------------------------
|
||||
kernel/trace/trace_probe.h | 7 ---
|
||||
kernel/trace/trace_uprobe.c | 2 +-
|
||||
6 files changed, 103 insertions(+), 103 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index ee24e0bfc391..f3d7c259e424 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -8280,6 +8280,92 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ftrace_dump);
|
||||
|
||||
+int trace_run_command(const char *buf, int (*createfn)(int, char **))
|
||||
+{
|
||||
+ char **argv;
|
||||
+ int argc, ret;
|
||||
+
|
||||
+ argc = 0;
|
||||
+ ret = 0;
|
||||
+ argv = argv_split(GFP_KERNEL, buf, &argc);
|
||||
+ if (!argv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ if (argc)
|
||||
+ ret = createfn(argc, argv);
|
||||
+
|
||||
+ argv_free(argv);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+#define WRITE_BUFSIZE 4096
|
||||
+
|
||||
+ssize_t trace_parse_run_command(struct file *file, const char __user *buffer,
|
||||
+ size_t count, loff_t *ppos,
|
||||
+ int (*createfn)(int, char **))
|
||||
+{
|
||||
+ char *kbuf, *buf, *tmp;
|
||||
+ int ret = 0;
|
||||
+ size_t done = 0;
|
||||
+ size_t size;
|
||||
+
|
||||
+ kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
|
||||
+ if (!kbuf)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ while (done < count) {
|
||||
+ size = count - done;
|
||||
+
|
||||
+ if (size >= WRITE_BUFSIZE)
|
||||
+ size = WRITE_BUFSIZE - 1;
|
||||
+
|
||||
+ if (copy_from_user(kbuf, buffer + done, size)) {
|
||||
+ ret = -EFAULT;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ kbuf[size] = '\0';
|
||||
+ buf = kbuf;
|
||||
+ do {
|
||||
+ tmp = strchr(buf, '\n');
|
||||
+ if (tmp) {
|
||||
+ *tmp = '\0';
|
||||
+ size = tmp - buf + 1;
|
||||
+ } else {
|
||||
+ size = strlen(buf);
|
||||
+ if (done + size < count) {
|
||||
+ if (buf != kbuf)
|
||||
+ break;
|
||||
+ /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */
|
||||
+ pr_warn("Line length is too long: Should be less than %d\n",
|
||||
+ WRITE_BUFSIZE - 2);
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+ done += size;
|
||||
+
|
||||
+ /* Remove comments */
|
||||
+ tmp = strchr(buf, '#');
|
||||
+
|
||||
+ if (tmp)
|
||||
+ *tmp = '\0';
|
||||
+
|
||||
+ ret = trace_run_command(buf, createfn);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+ buf += size;
|
||||
+
|
||||
+ } while (done < count);
|
||||
+ }
|
||||
+ ret = done;
|
||||
+
|
||||
+out:
|
||||
+ kfree(kbuf);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
__init static int tracer_alloc_buffers(void)
|
||||
{
|
||||
int ring_buf_size;
|
||||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
|
||||
index 851cd1605085..221591636530 100644
|
||||
--- a/kernel/trace/trace.h
|
||||
+++ b/kernel/trace/trace.h
|
||||
@@ -1755,6 +1755,13 @@ void trace_printk_start_comm(void);
|
||||
int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set);
|
||||
int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled);
|
||||
|
||||
+#define MAX_EVENT_NAME_LEN 64
|
||||
+
|
||||
+extern int trace_run_command(const char *buf, int (*createfn)(int, char**));
|
||||
+extern ssize_t trace_parse_run_command(struct file *file,
|
||||
+ const char __user *buffer, size_t count, loff_t *ppos,
|
||||
+ int (*createfn)(int, char**));
|
||||
+
|
||||
/*
|
||||
* Normal trace_printk() and friends allocates special buffers
|
||||
* to do the manipulation, as well as saves the print formats
|
||||
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
|
||||
index ea20274a105a..3c40d4174052 100644
|
||||
--- a/kernel/trace/trace_kprobe.c
|
||||
+++ b/kernel/trace/trace_kprobe.c
|
||||
@@ -918,8 +918,8 @@ static int probes_open(struct inode *inode, struct file *file)
|
||||
static ssize_t probes_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
- return traceprobe_probes_write(file, buffer, count, ppos,
|
||||
- create_trace_kprobe);
|
||||
+ return trace_parse_run_command(file, buffer, count, ppos,
|
||||
+ create_trace_kprobe);
|
||||
}
|
||||
|
||||
static const struct file_operations kprobe_events_ops = {
|
||||
@@ -1444,9 +1444,9 @@ static __init int kprobe_trace_self_tests_init(void)
|
||||
|
||||
pr_info("Testing kprobe tracing: ");
|
||||
|
||||
- ret = traceprobe_command("p:testprobe kprobe_trace_selftest_target "
|
||||
- "$stack $stack0 +0($stack)",
|
||||
- create_trace_kprobe);
|
||||
+ ret = trace_run_command("p:testprobe kprobe_trace_selftest_target "
|
||||
+ "$stack $stack0 +0($stack)",
|
||||
+ create_trace_kprobe);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error on probing function entry.\n");
|
||||
warn++;
|
||||
@@ -1466,8 +1466,8 @@ static __init int kprobe_trace_self_tests_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
- ret = traceprobe_command("r:testprobe2 kprobe_trace_selftest_target "
|
||||
- "$retval", create_trace_kprobe);
|
||||
+ ret = trace_run_command("r:testprobe2 kprobe_trace_selftest_target "
|
||||
+ "$retval", create_trace_kprobe);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error on probing function return.\n");
|
||||
warn++;
|
||||
@@ -1537,13 +1537,13 @@ static __init int kprobe_trace_self_tests_init(void)
|
||||
disable_trace_kprobe(tk, file);
|
||||
}
|
||||
|
||||
- ret = traceprobe_command("-:testprobe", create_trace_kprobe);
|
||||
+ ret = trace_run_command("-:testprobe", create_trace_kprobe);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error on deleting a probe.\n");
|
||||
warn++;
|
||||
}
|
||||
|
||||
- ret = traceprobe_command("-:testprobe2", create_trace_kprobe);
|
||||
+ ret = trace_run_command("-:testprobe2", create_trace_kprobe);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
pr_warn("error on deleting a probe.\n");
|
||||
warn++;
|
||||
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
|
||||
index fe4513330412..daf54bda4dc8 100644
|
||||
--- a/kernel/trace/trace_probe.c
|
||||
+++ b/kernel/trace/trace_probe.c
|
||||
@@ -621,92 +621,6 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
|
||||
kfree(arg->comm);
|
||||
}
|
||||
|
||||
-int traceprobe_command(const char *buf, int (*createfn)(int, char **))
|
||||
-{
|
||||
- char **argv;
|
||||
- int argc, ret;
|
||||
-
|
||||
- argc = 0;
|
||||
- ret = 0;
|
||||
- argv = argv_split(GFP_KERNEL, buf, &argc);
|
||||
- if (!argv)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- if (argc)
|
||||
- ret = createfn(argc, argv);
|
||||
-
|
||||
- argv_free(argv);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-#define WRITE_BUFSIZE 4096
|
||||
-
|
||||
-ssize_t traceprobe_probes_write(struct file *file, const char __user *buffer,
|
||||
- size_t count, loff_t *ppos,
|
||||
- int (*createfn)(int, char **))
|
||||
-{
|
||||
- char *kbuf, *buf, *tmp;
|
||||
- int ret = 0;
|
||||
- size_t done = 0;
|
||||
- size_t size;
|
||||
-
|
||||
- kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
|
||||
- if (!kbuf)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- while (done < count) {
|
||||
- size = count - done;
|
||||
-
|
||||
- if (size >= WRITE_BUFSIZE)
|
||||
- size = WRITE_BUFSIZE - 1;
|
||||
-
|
||||
- if (copy_from_user(kbuf, buffer + done, size)) {
|
||||
- ret = -EFAULT;
|
||||
- goto out;
|
||||
- }
|
||||
- kbuf[size] = '\0';
|
||||
- buf = kbuf;
|
||||
- do {
|
||||
- tmp = strchr(buf, '\n');
|
||||
- if (tmp) {
|
||||
- *tmp = '\0';
|
||||
- size = tmp - buf + 1;
|
||||
- } else {
|
||||
- size = strlen(buf);
|
||||
- if (done + size < count) {
|
||||
- if (buf != kbuf)
|
||||
- break;
|
||||
- /* This can accept WRITE_BUFSIZE - 2 ('\n' + '\0') */
|
||||
- pr_warn("Line length is too long: Should be less than %d\n",
|
||||
- WRITE_BUFSIZE - 2);
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
- }
|
||||
- done += size;
|
||||
-
|
||||
- /* Remove comments */
|
||||
- tmp = strchr(buf, '#');
|
||||
-
|
||||
- if (tmp)
|
||||
- *tmp = '\0';
|
||||
-
|
||||
- ret = traceprobe_command(buf, createfn);
|
||||
- if (ret)
|
||||
- goto out;
|
||||
- buf += size;
|
||||
-
|
||||
- } while (done < count);
|
||||
- }
|
||||
- ret = done;
|
||||
-
|
||||
-out:
|
||||
- kfree(kbuf);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
|
||||
bool is_return)
|
||||
{
|
||||
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
|
||||
index dc39472ca9e4..a0d750e3d17c 100644
|
||||
--- a/kernel/trace/trace_probe.h
|
||||
+++ b/kernel/trace/trace_probe.h
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
#define MAX_TRACE_ARGS 128
|
||||
#define MAX_ARGSTR_LEN 63
|
||||
-#define MAX_EVENT_NAME_LEN 64
|
||||
#define MAX_STRING_SIZE PATH_MAX
|
||||
|
||||
/* Reserved field names */
|
||||
@@ -356,12 +355,6 @@ extern void traceprobe_free_probe_arg(struct probe_arg *arg);
|
||||
|
||||
extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
|
||||
|
||||
-extern ssize_t traceprobe_probes_write(struct file *file,
|
||||
- const char __user *buffer, size_t count, loff_t *ppos,
|
||||
- int (*createfn)(int, char**));
|
||||
-
|
||||
-extern int traceprobe_command(const char *buf, int (*createfn)(int, char**));
|
||||
-
|
||||
/* Sum up total data length for dynamic arraies (strings) */
|
||||
static nokprobe_inline int
|
||||
__get_data_size(struct trace_probe *tp, struct pt_regs *regs)
|
||||
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
|
||||
index ea0d90a31fc9..2ccfbb8efeb2 100644
|
||||
--- a/kernel/trace/trace_uprobe.c
|
||||
+++ b/kernel/trace/trace_uprobe.c
|
||||
@@ -647,7 +647,7 @@ static int probes_open(struct inode *inode, struct file *file)
|
||||
static ssize_t probes_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
- return traceprobe_probes_write(file, buffer, count, ppos, create_trace_uprobe);
|
||||
+ return trace_parse_run_command(file, buffer, count, ppos, create_trace_uprobe);
|
||||
}
|
||||
|
||||
static const struct file_operations uprobe_events_ops = {
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
From a10939ad451478d0da11c0b2fb7e77219a7fbd8f Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:21 -0500
|
||||
Subject: [PATCH 075/450] tracing: Clean up hist_field_flags enum
|
||||
|
||||
As we add more flags, specifying explicit integers for the flag values
|
||||
becomes more unwieldy and error-prone - switch them over to left-shift
|
||||
values.
|
||||
|
||||
Link: http://lkml.kernel.org/r/e644e4fb7665aec015f4a2d84a2f990d3dd5b8a1.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 0d7a8325bf3326c92da2d21b4496a9ddde896d4f)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 20 ++++++++++----------
|
||||
1 file changed, 10 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 7eb975a2d0e1..4f6b6406d6ec 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -110,16 +110,16 @@ DEFINE_HIST_FIELD_FN(u8);
|
||||
#define HIST_KEY_SIZE_MAX (MAX_FILTER_STR_VAL + HIST_STACKTRACE_SIZE)
|
||||
|
||||
enum hist_field_flags {
|
||||
- HIST_FIELD_FL_HITCOUNT = 1,
|
||||
- HIST_FIELD_FL_KEY = 2,
|
||||
- HIST_FIELD_FL_STRING = 4,
|
||||
- HIST_FIELD_FL_HEX = 8,
|
||||
- HIST_FIELD_FL_SYM = 16,
|
||||
- HIST_FIELD_FL_SYM_OFFSET = 32,
|
||||
- HIST_FIELD_FL_EXECNAME = 64,
|
||||
- HIST_FIELD_FL_SYSCALL = 128,
|
||||
- HIST_FIELD_FL_STACKTRACE = 256,
|
||||
- HIST_FIELD_FL_LOG2 = 512,
|
||||
+ HIST_FIELD_FL_HITCOUNT = 1 << 0,
|
||||
+ HIST_FIELD_FL_KEY = 1 << 1,
|
||||
+ HIST_FIELD_FL_STRING = 1 << 2,
|
||||
+ HIST_FIELD_FL_HEX = 1 << 3,
|
||||
+ HIST_FIELD_FL_SYM = 1 << 4,
|
||||
+ HIST_FIELD_FL_SYM_OFFSET = 1 << 5,
|
||||
+ HIST_FIELD_FL_EXECNAME = 1 << 6,
|
||||
+ HIST_FIELD_FL_SYSCALL = 1 << 7,
|
||||
+ HIST_FIELD_FL_STACKTRACE = 1 << 8,
|
||||
+ HIST_FIELD_FL_LOG2 = 1 << 9,
|
||||
};
|
||||
|
||||
struct hist_trigger_attrs {
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
From 5268e6c1c89091d12ad6e4f39733c0523498f42a Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:22 -0500
|
||||
Subject: [PATCH 076/450] tracing: Add hist_field_name() accessor
|
||||
|
||||
In preparation for hist_fields that won't be strictly based on
|
||||
trace_event_fields, add a new hist_field_name() accessor to allow that
|
||||
flexibility and update associated users.
|
||||
|
||||
Link: http://lkml.kernel.org/r/5b5a2d36dde067cbbe2434b10f06daac27b7dbd5.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 85013256cf01629f72a327674c5d007b4a4b40da)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 67 +++++++++++++++++++++-----------
|
||||
1 file changed, 45 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 4f6b6406d6ec..4dc39e3efb21 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -146,6 +146,23 @@ struct hist_trigger_data {
|
||||
struct tracing_map *map;
|
||||
};
|
||||
|
||||
+static const char *hist_field_name(struct hist_field *field,
|
||||
+ unsigned int level)
|
||||
+{
|
||||
+ const char *field_name = "";
|
||||
+
|
||||
+ if (level > 1)
|
||||
+ return field_name;
|
||||
+
|
||||
+ if (field->field)
|
||||
+ field_name = field->field->name;
|
||||
+
|
||||
+ if (field_name == NULL)
|
||||
+ field_name = "";
|
||||
+
|
||||
+ return field_name;
|
||||
+}
|
||||
+
|
||||
static hist_field_fn_t select_value_fn(int field_size, int field_is_signed)
|
||||
{
|
||||
hist_field_fn_t fn = NULL;
|
||||
@@ -653,7 +670,6 @@ static int is_descending(const char *str)
|
||||
static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
char *fields_str = hist_data->attrs->sort_key_str;
|
||||
- struct ftrace_event_field *field = NULL;
|
||||
struct tracing_map_sort_key *sort_key;
|
||||
int descending, ret = 0;
|
||||
unsigned int i, j;
|
||||
@@ -670,7 +686,9 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
}
|
||||
|
||||
for (i = 0; i < TRACING_MAP_SORT_KEYS_MAX; i++) {
|
||||
+ struct hist_field *hist_field;
|
||||
char *field_str, *field_name;
|
||||
+ const char *test_name;
|
||||
|
||||
sort_key = &hist_data->sort_keys[i];
|
||||
|
||||
@@ -703,8 +721,10 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
}
|
||||
|
||||
for (j = 1; j < hist_data->n_fields; j++) {
|
||||
- field = hist_data->fields[j]->field;
|
||||
- if (field && (strcmp(field_name, field->name) == 0)) {
|
||||
+ hist_field = hist_data->fields[j];
|
||||
+ test_name = hist_field_name(hist_field, 0);
|
||||
+
|
||||
+ if (strcmp(field_name, test_name) == 0) {
|
||||
sort_key->field_idx = j;
|
||||
descending = is_descending(field_str);
|
||||
if (descending < 0) {
|
||||
@@ -952,6 +972,7 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
struct hist_field *key_field;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
bool multiline = false;
|
||||
+ const char *field_name;
|
||||
unsigned int i;
|
||||
u64 uval;
|
||||
|
||||
@@ -963,26 +984,27 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
if (i > hist_data->n_vals)
|
||||
seq_puts(m, ", ");
|
||||
|
||||
+ field_name = hist_field_name(key_field, 0);
|
||||
+
|
||||
if (key_field->flags & HIST_FIELD_FL_HEX) {
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
- seq_printf(m, "%s: %llx",
|
||||
- key_field->field->name, uval);
|
||||
+ seq_printf(m, "%s: %llx", field_name, uval);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_SYM) {
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
sprint_symbol_no_offset(str, uval);
|
||||
- seq_printf(m, "%s: [%llx] %-45s",
|
||||
- key_field->field->name, uval, str);
|
||||
+ seq_printf(m, "%s: [%llx] %-45s", field_name,
|
||||
+ uval, str);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_SYM_OFFSET) {
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
sprint_symbol(str, uval);
|
||||
- seq_printf(m, "%s: [%llx] %-55s",
|
||||
- key_field->field->name, uval, str);
|
||||
+ seq_printf(m, "%s: [%llx] %-55s", field_name,
|
||||
+ uval, str);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
|
||||
char *comm = elt->private_data;
|
||||
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
- seq_printf(m, "%s: %-16s[%10llu]",
|
||||
- key_field->field->name, comm, uval);
|
||||
+ seq_printf(m, "%s: %-16s[%10llu]", field_name,
|
||||
+ comm, uval);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_SYSCALL) {
|
||||
const char *syscall_name;
|
||||
|
||||
@@ -991,8 +1013,8 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
if (!syscall_name)
|
||||
syscall_name = "unknown_syscall";
|
||||
|
||||
- seq_printf(m, "%s: %-30s[%3llu]",
|
||||
- key_field->field->name, syscall_name, uval);
|
||||
+ seq_printf(m, "%s: %-30s[%3llu]", field_name,
|
||||
+ syscall_name, uval);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_STACKTRACE) {
|
||||
seq_puts(m, "stacktrace:\n");
|
||||
hist_trigger_stacktrace_print(m,
|
||||
@@ -1000,15 +1022,14 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
HIST_STACKTRACE_DEPTH);
|
||||
multiline = true;
|
||||
} else if (key_field->flags & HIST_FIELD_FL_LOG2) {
|
||||
- seq_printf(m, "%s: ~ 2^%-2llu", key_field->field->name,
|
||||
+ seq_printf(m, "%s: ~ 2^%-2llu", field_name,
|
||||
*(u64 *)(key + key_field->offset));
|
||||
} else if (key_field->flags & HIST_FIELD_FL_STRING) {
|
||||
- seq_printf(m, "%s: %-50s", key_field->field->name,
|
||||
+ seq_printf(m, "%s: %-50s", field_name,
|
||||
(char *)(key + key_field->offset));
|
||||
} else {
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
- seq_printf(m, "%s: %10llu", key_field->field->name,
|
||||
- uval);
|
||||
+ seq_printf(m, "%s: %10llu", field_name, uval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,13 +1042,13 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
tracing_map_read_sum(elt, HITCOUNT_IDX));
|
||||
|
||||
for (i = 1; i < hist_data->n_vals; i++) {
|
||||
+ field_name = hist_field_name(hist_data->fields[i], 0);
|
||||
+
|
||||
if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) {
|
||||
- seq_printf(m, " %s: %10llx",
|
||||
- hist_data->fields[i]->field->name,
|
||||
+ seq_printf(m, " %s: %10llx", field_name,
|
||||
tracing_map_read_sum(elt, i));
|
||||
} else {
|
||||
- seq_printf(m, " %s: %10llu",
|
||||
- hist_data->fields[i]->field->name,
|
||||
+ seq_printf(m, " %s: %10llu", field_name,
|
||||
tracing_map_read_sum(elt, i));
|
||||
}
|
||||
}
|
||||
@@ -1142,7 +1163,9 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
|
||||
static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
||||
{
|
||||
- seq_printf(m, "%s", hist_field->field->name);
|
||||
+ const char *field_name = hist_field_name(hist_field, 0);
|
||||
+
|
||||
+ seq_printf(m, "%s", field_name);
|
||||
if (hist_field->flags) {
|
||||
const char *flags_str = get_hist_field_flags(hist_field);
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
From a26cd096a9541d39014f28705c6cdb351212b3c3 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Fri, 22 Sep 2017 14:58:23 -0500
|
||||
Subject: [PATCH 077/450] tracing: Reimplement log2
|
||||
|
||||
log2 as currently implemented applies only to u64 trace_event_field
|
||||
derived fields, and assumes that anything it's applied to is a u64
|
||||
field.
|
||||
|
||||
To prepare for synthetic fields like latencies, log2 should be
|
||||
applicable to those as well, so take the opportunity now to fix the
|
||||
current problems as well as expand to more general uses.
|
||||
|
||||
log2 should be thought of as a chaining function rather than a field
|
||||
type. To enable this as well as possible future function
|
||||
implementations, add a hist_field operand array into the hist_field
|
||||
definition for this purpose, and make use of it to implement the log2
|
||||
'function'.
|
||||
|
||||
Link: http://lkml.kernel.org/r/b47f93fc0b87b36eccf716b0c018f3a71e1f1111.1506105045.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 5819eaddf35b24d628ddfa4fbb5f8d4026e44b96)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 31 +++++++++++++++++++++++++++----
|
||||
1 file changed, 27 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 4dc39e3efb21..4eddc1933079 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -28,12 +28,16 @@ struct hist_field;
|
||||
|
||||
typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
|
||||
|
||||
+#define HIST_FIELD_OPERANDS_MAX 2
|
||||
+
|
||||
struct hist_field {
|
||||
struct ftrace_event_field *field;
|
||||
unsigned long flags;
|
||||
hist_field_fn_t fn;
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
+ unsigned int is_signed;
|
||||
+ struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
};
|
||||
|
||||
static u64 hist_field_none(struct hist_field *field, void *event)
|
||||
@@ -71,7 +75,9 @@ static u64 hist_field_pstring(struct hist_field *hist_field, void *event)
|
||||
|
||||
static u64 hist_field_log2(struct hist_field *hist_field, void *event)
|
||||
{
|
||||
- u64 val = *(u64 *)(event + hist_field->field->offset);
|
||||
+ struct hist_field *operand = hist_field->operands[0];
|
||||
+
|
||||
+ u64 val = operand->fn(operand, event);
|
||||
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
@@ -156,6 +162,8 @@ static const char *hist_field_name(struct hist_field *field,
|
||||
|
||||
if (field->field)
|
||||
field_name = field->field->name;
|
||||
+ else if (field->flags & HIST_FIELD_FL_LOG2)
|
||||
+ field_name = hist_field_name(field->operands[0], ++level);
|
||||
|
||||
if (field_name == NULL)
|
||||
field_name = "";
|
||||
@@ -357,8 +365,20 @@ static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
|
||||
.elt_init = hist_trigger_elt_comm_init,
|
||||
};
|
||||
|
||||
-static void destroy_hist_field(struct hist_field *hist_field)
|
||||
+static void destroy_hist_field(struct hist_field *hist_field,
|
||||
+ unsigned int level)
|
||||
{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ if (level > 2)
|
||||
+ return;
|
||||
+
|
||||
+ if (!hist_field)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
|
||||
+ destroy_hist_field(hist_field->operands[i], level + 1);
|
||||
+
|
||||
kfree(hist_field);
|
||||
}
|
||||
|
||||
@@ -385,7 +405,10 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
}
|
||||
|
||||
if (flags & HIST_FIELD_FL_LOG2) {
|
||||
+ unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
|
||||
hist_field->fn = hist_field_log2;
|
||||
+ hist_field->operands[0] = create_hist_field(field, fl);
|
||||
+ hist_field->size = hist_field->operands[0]->size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -405,7 +428,7 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
hist_field->fn = select_value_fn(field->size,
|
||||
field->is_signed);
|
||||
if (!hist_field->fn) {
|
||||
- destroy_hist_field(hist_field);
|
||||
+ destroy_hist_field(hist_field, 0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -422,7 +445,7 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
|
||||
for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) {
|
||||
if (hist_data->fields[i]) {
|
||||
- destroy_hist_field(hist_data->fields[i]);
|
||||
+ destroy_hist_field(hist_data->fields[i], 0);
|
||||
hist_data->fields[i] = NULL;
|
||||
}
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
From cdc25f0d6e106121a6dd599495ad7f00f766ca49 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:36 -0600
|
||||
Subject: [PATCH 079/450] tracing: Add Documentation for log2 modifier
|
||||
|
||||
Add a line for the log2 modifier, to keep it aligned with
|
||||
tracing/README.
|
||||
|
||||
Link: http://lkml.kernel.org/r/a419028bccab155749a4b8702d5b97af75f1578f.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit fcb5b95a2bb931f8e72e2dbd2def67382dd99d42)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
Documentation/trace/histogram.txt | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/Documentation/trace/histogram.txt b/Documentation/trace/histogram.txt
|
||||
index b2145f44b190..a4143f04a097 100644
|
||||
--- a/Documentation/trace/histogram.txt
|
||||
+++ b/Documentation/trace/histogram.txt
|
||||
@@ -73,6 +73,7 @@
|
||||
.sym-offset display an address as a symbol and offset
|
||||
.syscall display a syscall id as a system call name
|
||||
.execname display a common_pid as a program name
|
||||
+ .log2 display log2 value rather than raw number
|
||||
|
||||
Note that in general the semantics of a given field aren't
|
||||
interpreted when applying a modifier to it, but there are some
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
From d8196bc0e2751a637e8eacbbd15ae20322b2b74d Mon Sep 17 00:00:00 2001
|
||||
From: Vedang Patel <vedang.patel@intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:37 -0600
|
||||
Subject: [PATCH 080/450] tracing: Add support to detect and avoid duplicates
|
||||
|
||||
A duplicate in the tracing_map hash table is when 2 different entries
|
||||
have the same key and, as a result, the key_hash. This is possible due
|
||||
to a race condition in the algorithm. This race condition is inherent to
|
||||
the algorithm and not a bug. This was fine because, until now, we were
|
||||
only interested in the sum of all the values related to a particular
|
||||
key (the duplicates are dealt with in tracing_map_sort_entries()). But,
|
||||
with the inclusion of variables[1], we are interested in individual
|
||||
values. So, it will not be clear what value to choose when
|
||||
there are duplicates. So, the duplicates need to be removed.
|
||||
|
||||
The duplicates can occur in the code in the following scenarios:
|
||||
|
||||
- A thread is in the process of adding a new element. It has
|
||||
successfully executed cmpxchg() and inserted the key. But, it is still
|
||||
not done acquiring the trace_map_elt struct, populating it and storing
|
||||
the pointer to the struct in the value field of tracing_map hash table.
|
||||
If another thread comes in at this time and wants to add an element with
|
||||
the same key, it will not see the current element and add a new one.
|
||||
|
||||
- There are multiple threads trying to execute cmpxchg at the same time,
|
||||
one of the threads will succeed and the others will fail. The ones which
|
||||
fail will go ahead increment 'idx' and add a new element there creating
|
||||
a duplicate.
|
||||
|
||||
This patch detects and avoids the first condition by asking the thread
|
||||
which detects the duplicate to loop one more time. There is also a
|
||||
possibility of infinite loop if the thread which is trying to insert
|
||||
goes to sleep indefinitely and the one which is trying to insert a new
|
||||
element detects a duplicate. Which is why, the thread loops for
|
||||
map_size iterations before returning NULL.
|
||||
|
||||
The second scenario is avoided by preventing the threads which failed
|
||||
cmpxchg() from incrementing idx. This way, they will loop
|
||||
around and check if the thread which succeeded in executing cmpxchg()
|
||||
had the same key.
|
||||
|
||||
[1] http://lkml.kernel.org/r/cover.1498510759.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Link: http://lkml.kernel.org/r/e178e89ec399240331d383bd5913d649713110f4.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Vedang Patel <vedang.patel@intel.com>
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit bd0a7ab135d0d0872296c3ae3c4f816a9a4c3dee)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/tracing_map.c | 41 +++++++++++++++++++++++++++++++++-----
|
||||
1 file changed, 36 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
|
||||
index 07e75344725b..b30f3439f27f 100644
|
||||
--- a/kernel/trace/tracing_map.c
|
||||
+++ b/kernel/trace/tracing_map.c
|
||||
@@ -414,7 +414,9 @@ static inline struct tracing_map_elt *
|
||||
__tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
|
||||
{
|
||||
u32 idx, key_hash, test_key;
|
||||
+ int dup_try = 0;
|
||||
struct tracing_map_entry *entry;
|
||||
+ struct tracing_map_elt *val;
|
||||
|
||||
key_hash = jhash(key, map->key_size, 0);
|
||||
if (key_hash == 0)
|
||||
@@ -426,11 +428,33 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
|
||||
entry = TRACING_MAP_ENTRY(map->map, idx);
|
||||
test_key = entry->key;
|
||||
|
||||
- if (test_key && test_key == key_hash && entry->val &&
|
||||
- keys_match(key, entry->val->key, map->key_size)) {
|
||||
- if (!lookup_only)
|
||||
- atomic64_inc(&map->hits);
|
||||
- return entry->val;
|
||||
+ if (test_key && test_key == key_hash) {
|
||||
+ val = READ_ONCE(entry->val);
|
||||
+ if (val &&
|
||||
+ keys_match(key, val->key, map->key_size)) {
|
||||
+ if (!lookup_only)
|
||||
+ atomic64_inc(&map->hits);
|
||||
+ return val;
|
||||
+ } else if (unlikely(!val)) {
|
||||
+ /*
|
||||
+ * The key is present. But, val (pointer to elt
|
||||
+ * struct) is still NULL. which means some other
|
||||
+ * thread is in the process of inserting an
|
||||
+ * element.
|
||||
+ *
|
||||
+ * On top of that, it's key_hash is same as the
|
||||
+ * one being inserted right now. So, it's
|
||||
+ * possible that the element has the same
|
||||
+ * key as well.
|
||||
+ */
|
||||
+
|
||||
+ dup_try++;
|
||||
+ if (dup_try > map->map_size) {
|
||||
+ atomic64_inc(&map->drops);
|
||||
+ break;
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (!test_key) {
|
||||
@@ -452,6 +476,13 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
|
||||
atomic64_inc(&map->hits);
|
||||
|
||||
return entry->val;
|
||||
+ } else {
|
||||
+ /*
|
||||
+ * cmpxchg() failed. Loop around once
|
||||
+ * more to check what key was inserted.
|
||||
+ */
|
||||
+ dup_try++;
|
||||
+ continue;
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
From 6139f982902cdb12bbbf875895de16575c41abc3 Mon Sep 17 00:00:00 2001
|
||||
From: Vedang Patel <vedang.patel@intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:38 -0600
|
||||
Subject: [PATCH 081/450] tracing: Remove code which merges duplicates
|
||||
|
||||
We now have the logic to detect and remove duplicates in the
|
||||
tracing_map hash table. The code which merges duplicates in the
|
||||
histogram is redundant now. So, modify this code just to detect
|
||||
duplicates. The duplication detection code is still kept to ensure
|
||||
that any rare race condition which might cause duplicates does not go
|
||||
unnoticed.
|
||||
|
||||
Link: http://lkml.kernel.org/r/55215cf59e2674391bdaf772fdafc4c393352b03.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Vedang Patel <vedang.patel@intel.com>
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 3f7f4cc21fc62ff7da7d34b5ca95a69d73a1f764)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 11 -----
|
||||
kernel/trace/tracing_map.c | 83 +++-----------------------------
|
||||
kernel/trace/tracing_map.h | 7 ---
|
||||
3 files changed, 6 insertions(+), 95 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 4eddc1933079..c57e5369b0c2 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -340,16 +340,6 @@ static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void hist_trigger_elt_comm_copy(struct tracing_map_elt *to,
|
||||
- struct tracing_map_elt *from)
|
||||
-{
|
||||
- char *comm_from = from->private_data;
|
||||
- char *comm_to = to->private_data;
|
||||
-
|
||||
- if (comm_from)
|
||||
- memcpy(comm_to, comm_from, TASK_COMM_LEN + 1);
|
||||
-}
|
||||
-
|
||||
static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
|
||||
{
|
||||
char *comm = elt->private_data;
|
||||
@@ -360,7 +350,6 @@ static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
|
||||
|
||||
static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
|
||||
.elt_alloc = hist_trigger_elt_comm_alloc,
|
||||
- .elt_copy = hist_trigger_elt_comm_copy,
|
||||
.elt_free = hist_trigger_elt_comm_free,
|
||||
.elt_init = hist_trigger_elt_comm_init,
|
||||
};
|
||||
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
|
||||
index b30f3439f27f..f47a4d54bcf0 100644
|
||||
--- a/kernel/trace/tracing_map.c
|
||||
+++ b/kernel/trace/tracing_map.c
|
||||
@@ -847,67 +847,15 @@ create_sort_entry(void *key, struct tracing_map_elt *elt)
|
||||
return sort_entry;
|
||||
}
|
||||
|
||||
-static struct tracing_map_elt *copy_elt(struct tracing_map_elt *elt)
|
||||
-{
|
||||
- struct tracing_map_elt *dup_elt;
|
||||
- unsigned int i;
|
||||
-
|
||||
- dup_elt = tracing_map_elt_alloc(elt->map);
|
||||
- if (IS_ERR(dup_elt))
|
||||
- return NULL;
|
||||
-
|
||||
- if (elt->map->ops && elt->map->ops->elt_copy)
|
||||
- elt->map->ops->elt_copy(dup_elt, elt);
|
||||
-
|
||||
- dup_elt->private_data = elt->private_data;
|
||||
- memcpy(dup_elt->key, elt->key, elt->map->key_size);
|
||||
-
|
||||
- for (i = 0; i < elt->map->n_fields; i++) {
|
||||
- atomic64_set(&dup_elt->fields[i].sum,
|
||||
- atomic64_read(&elt->fields[i].sum));
|
||||
- dup_elt->fields[i].cmp_fn = elt->fields[i].cmp_fn;
|
||||
- }
|
||||
-
|
||||
- return dup_elt;
|
||||
-}
|
||||
-
|
||||
-static int merge_dup(struct tracing_map_sort_entry **sort_entries,
|
||||
- unsigned int target, unsigned int dup)
|
||||
-{
|
||||
- struct tracing_map_elt *target_elt, *elt;
|
||||
- bool first_dup = (target - dup) == 1;
|
||||
- int i;
|
||||
-
|
||||
- if (first_dup) {
|
||||
- elt = sort_entries[target]->elt;
|
||||
- target_elt = copy_elt(elt);
|
||||
- if (!target_elt)
|
||||
- return -ENOMEM;
|
||||
- sort_entries[target]->elt = target_elt;
|
||||
- sort_entries[target]->elt_copied = true;
|
||||
- } else
|
||||
- target_elt = sort_entries[target]->elt;
|
||||
-
|
||||
- elt = sort_entries[dup]->elt;
|
||||
-
|
||||
- for (i = 0; i < elt->map->n_fields; i++)
|
||||
- atomic64_add(atomic64_read(&elt->fields[i].sum),
|
||||
- &target_elt->fields[i].sum);
|
||||
-
|
||||
- sort_entries[dup]->dup = true;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int merge_dups(struct tracing_map_sort_entry **sort_entries,
|
||||
+static void detect_dups(struct tracing_map_sort_entry **sort_entries,
|
||||
int n_entries, unsigned int key_size)
|
||||
{
|
||||
unsigned int dups = 0, total_dups = 0;
|
||||
- int err, i, j;
|
||||
+ int i;
|
||||
void *key;
|
||||
|
||||
if (n_entries < 2)
|
||||
- return total_dups;
|
||||
+ return;
|
||||
|
||||
sort(sort_entries, n_entries, sizeof(struct tracing_map_sort_entry *),
|
||||
(int (*)(const void *, const void *))cmp_entries_dup, NULL);
|
||||
@@ -916,30 +864,14 @@ static int merge_dups(struct tracing_map_sort_entry **sort_entries,
|
||||
for (i = 1; i < n_entries; i++) {
|
||||
if (!memcmp(sort_entries[i]->key, key, key_size)) {
|
||||
dups++; total_dups++;
|
||||
- err = merge_dup(sort_entries, i - dups, i);
|
||||
- if (err)
|
||||
- return err;
|
||||
continue;
|
||||
}
|
||||
key = sort_entries[i]->key;
|
||||
dups = 0;
|
||||
}
|
||||
|
||||
- if (!total_dups)
|
||||
- return total_dups;
|
||||
-
|
||||
- for (i = 0, j = 0; i < n_entries; i++) {
|
||||
- if (!sort_entries[i]->dup) {
|
||||
- sort_entries[j] = sort_entries[i];
|
||||
- if (j++ != i)
|
||||
- sort_entries[i] = NULL;
|
||||
- } else {
|
||||
- destroy_sort_entry(sort_entries[i]);
|
||||
- sort_entries[i] = NULL;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return total_dups;
|
||||
+ WARN_ONCE(total_dups > 0,
|
||||
+ "Duplicates detected: %d\n", total_dups);
|
||||
}
|
||||
|
||||
static bool is_key(struct tracing_map *map, unsigned int field_idx)
|
||||
@@ -1065,10 +997,7 @@ int tracing_map_sort_entries(struct tracing_map *map,
|
||||
return 1;
|
||||
}
|
||||
|
||||
- ret = merge_dups(entries, n_entries, map->key_size);
|
||||
- if (ret < 0)
|
||||
- goto free;
|
||||
- n_entries -= ret;
|
||||
+ detect_dups(entries, n_entries, map->key_size);
|
||||
|
||||
if (is_key(map, sort_keys[0].field_idx))
|
||||
cmp_entries_fn = cmp_entries_key;
|
||||
diff --git a/kernel/trace/tracing_map.h b/kernel/trace/tracing_map.h
|
||||
index 5b5bbf8ae550..de57887c0670 100644
|
||||
--- a/kernel/trace/tracing_map.h
|
||||
+++ b/kernel/trace/tracing_map.h
|
||||
@@ -215,11 +215,6 @@ struct tracing_map {
|
||||
* Element allocation occurs before tracing begins, when the
|
||||
* tracing_map_init() call is made by client code.
|
||||
*
|
||||
- * @elt_copy: At certain points in the lifetime of an element, it may
|
||||
- * need to be copied. The copy should include a copy of the
|
||||
- * client-allocated data, which can be copied into the 'to'
|
||||
- * element from the 'from' element.
|
||||
- *
|
||||
* @elt_free: When a tracing_map_elt is freed, this function is called
|
||||
* and allows client-allocated per-element data to be freed.
|
||||
*
|
||||
@@ -233,8 +228,6 @@ struct tracing_map {
|
||||
*/
|
||||
struct tracing_map_ops {
|
||||
int (*elt_alloc)(struct tracing_map_elt *elt);
|
||||
- void (*elt_copy)(struct tracing_map_elt *to,
|
||||
- struct tracing_map_elt *from);
|
||||
void (*elt_free)(struct tracing_map_elt *elt);
|
||||
void (*elt_clear)(struct tracing_map_elt *elt);
|
||||
void (*elt_init)(struct tracing_map_elt *elt);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
From 582fb702bc2b0544b208b2a5990dadf00da3e486 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:39 -0600
|
||||
Subject: [PATCH 082/450] ring-buffer: Add interface for setting absolute time
|
||||
stamps
|
||||
|
||||
Define a new function, tracing_set_time_stamp_abs(), which can be used
|
||||
to enable or disable the use of absolute timestamps rather than time
|
||||
deltas for a trace array.
|
||||
|
||||
Only the interface is added here; a subsequent patch will add the
|
||||
underlying implementation.
|
||||
|
||||
Link: http://lkml.kernel.org/r/ce96119de44c7fe0ee44786d15254e9b493040d3.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Baohong Liu <baohong.liu@intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 22753475c5232cd6f024746d6a6696a4dd2683ab)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/ring_buffer.h | 2 ++
|
||||
kernel/trace/ring_buffer.c | 11 +++++++++++
|
||||
kernel/trace/trace.c | 33 ++++++++++++++++++++++++++++++++-
|
||||
kernel/trace/trace.h | 3 +++
|
||||
4 files changed, 48 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
|
||||
index 5caa062a02b2..adffb56bae8f 100644
|
||||
--- a/include/linux/ring_buffer.h
|
||||
+++ b/include/linux/ring_buffer.h
|
||||
@@ -179,6 +179,8 @@ void ring_buffer_normalize_time_stamp(struct ring_buffer *buffer,
|
||||
int cpu, u64 *ts);
|
||||
void ring_buffer_set_clock(struct ring_buffer *buffer,
|
||||
u64 (*clock)(void));
|
||||
+void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs);
|
||||
+bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer);
|
||||
|
||||
size_t ring_buffer_page_len(void *page);
|
||||
|
||||
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
|
||||
index ade8493cb69f..c74b329879ad 100644
|
||||
--- a/kernel/trace/ring_buffer.c
|
||||
+++ b/kernel/trace/ring_buffer.c
|
||||
@@ -488,6 +488,7 @@ struct ring_buffer {
|
||||
u64 (*clock)(void);
|
||||
|
||||
struct rb_irq_work irq_work;
|
||||
+ bool time_stamp_abs;
|
||||
};
|
||||
|
||||
struct ring_buffer_iter {
|
||||
@@ -1387,6 +1388,16 @@ void ring_buffer_set_clock(struct ring_buffer *buffer,
|
||||
buffer->clock = clock;
|
||||
}
|
||||
|
||||
+void ring_buffer_set_time_stamp_abs(struct ring_buffer *buffer, bool abs)
|
||||
+{
|
||||
+ buffer->time_stamp_abs = abs;
|
||||
+}
|
||||
+
|
||||
+bool ring_buffer_time_stamp_abs(struct ring_buffer *buffer)
|
||||
+{
|
||||
+ return buffer->time_stamp_abs;
|
||||
+}
|
||||
+
|
||||
static void rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer);
|
||||
|
||||
static inline unsigned long rb_page_entries(struct buffer_page *bpage)
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index f3d7c259e424..ffb67272b642 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -2275,7 +2275,7 @@ trace_event_buffer_lock_reserve(struct ring_buffer **current_rb,
|
||||
|
||||
*current_rb = trace_file->tr->trace_buffer.buffer;
|
||||
|
||||
- if ((trace_file->flags &
|
||||
+ if (!ring_buffer_time_stamp_abs(*current_rb) && (trace_file->flags &
|
||||
(EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED)) &&
|
||||
(entry = this_cpu_read(trace_buffered_event))) {
|
||||
/* Try to use the per cpu buffer first */
|
||||
@@ -6298,6 +6298,37 @@ static int tracing_clock_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
+
|
||||
+ if (abs && tr->time_stamp_abs_ref++)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (!abs) {
|
||||
+ if (WARN_ON_ONCE(!tr->time_stamp_abs_ref)) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (--tr->time_stamp_abs_ref)
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ring_buffer_set_time_stamp_abs(tr->trace_buffer.buffer, abs);
|
||||
+
|
||||
+#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
+ if (tr->max_buffer.buffer)
|
||||
+ ring_buffer_set_time_stamp_abs(tr->max_buffer.buffer, abs);
|
||||
+#endif
|
||||
+ out:
|
||||
+ mutex_unlock(&trace_types_lock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
struct ftrace_buffer_info {
|
||||
struct trace_iterator iter;
|
||||
void *spare;
|
||||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
|
||||
index 221591636530..0506c4cdfeba 100644
|
||||
--- a/kernel/trace/trace.h
|
||||
+++ b/kernel/trace/trace.h
|
||||
@@ -273,6 +273,7 @@ struct trace_array {
|
||||
/* function tracing enabled */
|
||||
int function_enabled;
|
||||
#endif
|
||||
+ int time_stamp_abs_ref;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -286,6 +287,8 @@ extern struct mutex trace_types_lock;
|
||||
extern int trace_array_get(struct trace_array *tr);
|
||||
extern void trace_array_put(struct trace_array *tr);
|
||||
|
||||
+extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs);
|
||||
+
|
||||
/*
|
||||
* The global tracer (top) should be the first trace array added,
|
||||
* but we check the flag anyway.
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
From 64d14090ceefe8b55ecdcb0d3543fec1d830920c Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:40 -0600
|
||||
Subject: [PATCH 083/450] ring-buffer: Redefine the unimplemented
|
||||
RINGBUF_TYPE_TIME_STAMP
|
||||
|
||||
RINGBUF_TYPE_TIME_STAMP is defined but not used, and from what I can
|
||||
gather was reserved for something like an absolute timestamp feature
|
||||
for the ring buffer, if not a complete replacement of the current
|
||||
time_delta scheme.
|
||||
|
||||
This code redefines RINGBUF_TYPE_TIME_STAMP to implement absolute time
|
||||
stamps. Another way to look at it is that it essentially forces
|
||||
extended time_deltas for all events.
|
||||
|
||||
The motivation for doing this is to enable time_deltas that aren't
|
||||
dependent on previous events in the ring buffer, making it feasible to
|
||||
use the ring_buffer_event timetamps in a more random-access way, for
|
||||
purposes other than serial event printing.
|
||||
|
||||
To set/reset this mode, use tracing_set_timestamp_abs() from the
|
||||
previous interface patch.
|
||||
|
||||
Link: http://lkml.kernel.org/r/477b362dba1ce7fab9889a1a8e885a62c472f041.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 58c0bd803060b0c0c9de8751382a7af5f507d74d)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/ring_buffer.h | 12 +++--
|
||||
kernel/trace/ring_buffer.c | 104 ++++++++++++++++++++++++++----------
|
||||
2 files changed, 83 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h
|
||||
index adffb56bae8f..6c2a6b3f3c6d 100644
|
||||
--- a/include/linux/ring_buffer.h
|
||||
+++ b/include/linux/ring_buffer.h
|
||||
@@ -34,10 +34,12 @@ struct ring_buffer_event {
|
||||
* array[0] = time delta (28 .. 59)
|
||||
* size = 8 bytes
|
||||
*
|
||||
- * @RINGBUF_TYPE_TIME_STAMP: Sync time stamp with external clock
|
||||
- * array[0] = tv_nsec
|
||||
- * array[1..2] = tv_sec
|
||||
- * size = 16 bytes
|
||||
+ * @RINGBUF_TYPE_TIME_STAMP: Absolute timestamp
|
||||
+ * Same format as TIME_EXTEND except that the
|
||||
+ * value is an absolute timestamp, not a delta
|
||||
+ * event.time_delta contains bottom 27 bits
|
||||
+ * array[0] = top (28 .. 59) bits
|
||||
+ * size = 8 bytes
|
||||
*
|
||||
* <= @RINGBUF_TYPE_DATA_TYPE_LEN_MAX:
|
||||
* Data record
|
||||
@@ -54,12 +56,12 @@ enum ring_buffer_type {
|
||||
RINGBUF_TYPE_DATA_TYPE_LEN_MAX = 28,
|
||||
RINGBUF_TYPE_PADDING,
|
||||
RINGBUF_TYPE_TIME_EXTEND,
|
||||
- /* FIXME: RINGBUF_TYPE_TIME_STAMP not implemented */
|
||||
RINGBUF_TYPE_TIME_STAMP,
|
||||
};
|
||||
|
||||
unsigned ring_buffer_event_length(struct ring_buffer_event *event);
|
||||
void *ring_buffer_event_data(struct ring_buffer_event *event);
|
||||
+u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event);
|
||||
|
||||
/*
|
||||
* ring_buffer_discard_commit will remove an event that has not
|
||||
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
|
||||
index c74b329879ad..197617001c0b 100644
|
||||
--- a/kernel/trace/ring_buffer.c
|
||||
+++ b/kernel/trace/ring_buffer.c
|
||||
@@ -41,6 +41,8 @@ int ring_buffer_print_entry_header(struct trace_seq *s)
|
||||
RINGBUF_TYPE_PADDING);
|
||||
trace_seq_printf(s, "\ttime_extend : type == %d\n",
|
||||
RINGBUF_TYPE_TIME_EXTEND);
|
||||
+ trace_seq_printf(s, "\ttime_stamp : type == %d\n",
|
||||
+ RINGBUF_TYPE_TIME_STAMP);
|
||||
trace_seq_printf(s, "\tdata max type_len == %d\n",
|
||||
RINGBUF_TYPE_DATA_TYPE_LEN_MAX);
|
||||
|
||||
@@ -140,12 +142,15 @@ int ring_buffer_print_entry_header(struct trace_seq *s)
|
||||
|
||||
enum {
|
||||
RB_LEN_TIME_EXTEND = 8,
|
||||
- RB_LEN_TIME_STAMP = 16,
|
||||
+ RB_LEN_TIME_STAMP = 8,
|
||||
};
|
||||
|
||||
#define skip_time_extend(event) \
|
||||
((struct ring_buffer_event *)((char *)event + RB_LEN_TIME_EXTEND))
|
||||
|
||||
+#define extended_time(event) \
|
||||
+ (event->type_len >= RINGBUF_TYPE_TIME_EXTEND)
|
||||
+
|
||||
static inline int rb_null_event(struct ring_buffer_event *event)
|
||||
{
|
||||
return event->type_len == RINGBUF_TYPE_PADDING && !event->time_delta;
|
||||
@@ -209,7 +214,7 @@ rb_event_ts_length(struct ring_buffer_event *event)
|
||||
{
|
||||
unsigned len = 0;
|
||||
|
||||
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) {
|
||||
+ if (extended_time(event)) {
|
||||
/* time extends include the data event after it */
|
||||
len = RB_LEN_TIME_EXTEND;
|
||||
event = skip_time_extend(event);
|
||||
@@ -231,7 +236,7 @@ unsigned ring_buffer_event_length(struct ring_buffer_event *event)
|
||||
{
|
||||
unsigned length;
|
||||
|
||||
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
|
||||
+ if (extended_time(event))
|
||||
event = skip_time_extend(event);
|
||||
|
||||
length = rb_event_length(event);
|
||||
@@ -248,7 +253,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_length);
|
||||
static __always_inline void *
|
||||
rb_event_data(struct ring_buffer_event *event)
|
||||
{
|
||||
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
|
||||
+ if (extended_time(event))
|
||||
event = skip_time_extend(event);
|
||||
BUG_ON(event->type_len > RINGBUF_TYPE_DATA_TYPE_LEN_MAX);
|
||||
/* If length is in len field, then array[0] has the data */
|
||||
@@ -275,6 +280,27 @@ EXPORT_SYMBOL_GPL(ring_buffer_event_data);
|
||||
#define TS_MASK ((1ULL << TS_SHIFT) - 1)
|
||||
#define TS_DELTA_TEST (~TS_MASK)
|
||||
|
||||
+/**
|
||||
+ * ring_buffer_event_time_stamp - return the event's extended timestamp
|
||||
+ * @event: the event to get the timestamp of
|
||||
+ *
|
||||
+ * Returns the extended timestamp associated with a data event.
|
||||
+ * An extended time_stamp is a 64-bit timestamp represented
|
||||
+ * internally in a special way that makes the best use of space
|
||||
+ * contained within a ring buffer event. This function decodes
|
||||
+ * it and maps it to a straight u64 value.
|
||||
+ */
|
||||
+u64 ring_buffer_event_time_stamp(struct ring_buffer_event *event)
|
||||
+{
|
||||
+ u64 ts;
|
||||
+
|
||||
+ ts = event->array[0];
|
||||
+ ts <<= TS_SHIFT;
|
||||
+ ts += event->time_delta;
|
||||
+
|
||||
+ return ts;
|
||||
+}
|
||||
+
|
||||
/* Flag when events were overwritten */
|
||||
#define RB_MISSED_EVENTS (1 << 31)
|
||||
/* Missed count stored at end */
|
||||
@@ -2230,12 +2256,15 @@ rb_move_tail(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
|
||||
/* Slow path, do not inline */
|
||||
static noinline struct ring_buffer_event *
|
||||
-rb_add_time_stamp(struct ring_buffer_event *event, u64 delta)
|
||||
+rb_add_time_stamp(struct ring_buffer_event *event, u64 delta, bool abs)
|
||||
{
|
||||
- event->type_len = RINGBUF_TYPE_TIME_EXTEND;
|
||||
+ if (abs)
|
||||
+ event->type_len = RINGBUF_TYPE_TIME_STAMP;
|
||||
+ else
|
||||
+ event->type_len = RINGBUF_TYPE_TIME_EXTEND;
|
||||
|
||||
- /* Not the first event on the page? */
|
||||
- if (rb_event_index(event)) {
|
||||
+ /* Not the first event on the page, or not delta? */
|
||||
+ if (abs || rb_event_index(event)) {
|
||||
event->time_delta = delta & TS_MASK;
|
||||
event->array[0] = delta >> TS_SHIFT;
|
||||
} else {
|
||||
@@ -2278,7 +2307,9 @@ rb_update_event(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
* add it to the start of the resevered space.
|
||||
*/
|
||||
if (unlikely(info->add_timestamp)) {
|
||||
- event = rb_add_time_stamp(event, delta);
|
||||
+ bool abs = ring_buffer_time_stamp_abs(cpu_buffer->buffer);
|
||||
+
|
||||
+ event = rb_add_time_stamp(event, info->delta, abs);
|
||||
length -= RB_LEN_TIME_EXTEND;
|
||||
delta = 0;
|
||||
}
|
||||
@@ -2466,7 +2497,7 @@ static __always_inline void rb_end_commit(struct ring_buffer_per_cpu *cpu_buffer
|
||||
|
||||
static inline void rb_event_discard(struct ring_buffer_event *event)
|
||||
{
|
||||
- if (event->type_len == RINGBUF_TYPE_TIME_EXTEND)
|
||||
+ if (extended_time(event))
|
||||
event = skip_time_extend(event);
|
||||
|
||||
/* array[0] holds the actual length for the discarded event */
|
||||
@@ -2510,10 +2541,11 @@ rb_update_write_stamp(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
cpu_buffer->write_stamp =
|
||||
cpu_buffer->commit_page->page->time_stamp;
|
||||
else if (event->type_len == RINGBUF_TYPE_TIME_EXTEND) {
|
||||
- delta = event->array[0];
|
||||
- delta <<= TS_SHIFT;
|
||||
- delta += event->time_delta;
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
cpu_buffer->write_stamp += delta;
|
||||
+ } else if (event->type_len == RINGBUF_TYPE_TIME_STAMP) {
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
+ cpu_buffer->write_stamp = delta;
|
||||
} else
|
||||
cpu_buffer->write_stamp += event->time_delta;
|
||||
}
|
||||
@@ -2666,7 +2698,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
* If this is the first commit on the page, then it has the same
|
||||
* timestamp as the page itself.
|
||||
*/
|
||||
- if (!tail)
|
||||
+ if (!tail && !ring_buffer_time_stamp_abs(cpu_buffer->buffer))
|
||||
info->delta = 0;
|
||||
|
||||
/* See if we shot pass the end of this buffer page */
|
||||
@@ -2743,8 +2775,11 @@ rb_reserve_next_event(struct ring_buffer *buffer,
|
||||
/* make sure this diff is calculated here */
|
||||
barrier();
|
||||
|
||||
- /* Did the write stamp get updated already? */
|
||||
- if (likely(info.ts >= cpu_buffer->write_stamp)) {
|
||||
+ if (ring_buffer_time_stamp_abs(buffer)) {
|
||||
+ info.delta = info.ts;
|
||||
+ rb_handle_timestamp(cpu_buffer, &info);
|
||||
+ } else /* Did the write stamp get updated already? */
|
||||
+ if (likely(info.ts >= cpu_buffer->write_stamp)) {
|
||||
info.delta = diff;
|
||||
if (unlikely(test_time_stamp(info.delta)))
|
||||
rb_handle_timestamp(cpu_buffer, &info);
|
||||
@@ -3442,14 +3477,13 @@ rb_update_read_stamp(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_TIME_EXTEND:
|
||||
- delta = event->array[0];
|
||||
- delta <<= TS_SHIFT;
|
||||
- delta += event->time_delta;
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
cpu_buffer->read_stamp += delta;
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
- /* FIXME: not implemented */
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
+ cpu_buffer->read_stamp = delta;
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_DATA:
|
||||
@@ -3473,14 +3507,13 @@ rb_update_iter_read_stamp(struct ring_buffer_iter *iter,
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_TIME_EXTEND:
|
||||
- delta = event->array[0];
|
||||
- delta <<= TS_SHIFT;
|
||||
- delta += event->time_delta;
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
iter->read_stamp += delta;
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
- /* FIXME: not implemented */
|
||||
+ delta = ring_buffer_event_time_stamp(event);
|
||||
+ iter->read_stamp = delta;
|
||||
return;
|
||||
|
||||
case RINGBUF_TYPE_DATA:
|
||||
@@ -3704,6 +3737,8 @@ rb_buffer_peek(struct ring_buffer_per_cpu *cpu_buffer, u64 *ts,
|
||||
struct buffer_page *reader;
|
||||
int nr_loops = 0;
|
||||
|
||||
+ if (ts)
|
||||
+ *ts = 0;
|
||||
again:
|
||||
/*
|
||||
* We repeat when a time extend is encountered.
|
||||
@@ -3740,12 +3775,17 @@ rb_buffer_peek(struct ring_buffer_per_cpu *cpu_buffer, u64 *ts,
|
||||
goto again;
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
- /* FIXME: not implemented */
|
||||
+ if (ts) {
|
||||
+ *ts = ring_buffer_event_time_stamp(event);
|
||||
+ ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
|
||||
+ cpu_buffer->cpu, ts);
|
||||
+ }
|
||||
+ /* Internal data, OK to advance */
|
||||
rb_advance_reader(cpu_buffer);
|
||||
goto again;
|
||||
|
||||
case RINGBUF_TYPE_DATA:
|
||||
- if (ts) {
|
||||
+ if (ts && !(*ts)) {
|
||||
*ts = cpu_buffer->read_stamp + event->time_delta;
|
||||
ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
|
||||
cpu_buffer->cpu, ts);
|
||||
@@ -3770,6 +3810,9 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
struct ring_buffer_event *event;
|
||||
int nr_loops = 0;
|
||||
|
||||
+ if (ts)
|
||||
+ *ts = 0;
|
||||
+
|
||||
cpu_buffer = iter->cpu_buffer;
|
||||
buffer = cpu_buffer->buffer;
|
||||
|
||||
@@ -3822,12 +3865,17 @@ rb_iter_peek(struct ring_buffer_iter *iter, u64 *ts)
|
||||
goto again;
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
- /* FIXME: not implemented */
|
||||
+ if (ts) {
|
||||
+ *ts = ring_buffer_event_time_stamp(event);
|
||||
+ ring_buffer_normalize_time_stamp(cpu_buffer->buffer,
|
||||
+ cpu_buffer->cpu, ts);
|
||||
+ }
|
||||
+ /* Internal data, OK to advance */
|
||||
rb_advance_iter(iter);
|
||||
goto again;
|
||||
|
||||
case RINGBUF_TYPE_DATA:
|
||||
- if (ts) {
|
||||
+ if (ts && !(*ts)) {
|
||||
*ts = iter->read_stamp + event->time_delta;
|
||||
ring_buffer_normalize_time_stamp(buffer,
|
||||
cpu_buffer->cpu, ts);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
From 78df5faab56e9333581a9b048905776dcb52bb99 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:41 -0600
|
||||
Subject: [PATCH 084/450] tracing: Add timestamp_mode trace file
|
||||
|
||||
Add a new option flag indicating whether or not the ring buffer is in
|
||||
'absolute timestamp' mode.
|
||||
|
||||
Currently this is only set/unset by hist triggers that make use of a
|
||||
common_timestamp. As such, there's no reason to make this writeable
|
||||
for users - its purpose is only to allow users to determine
|
||||
unequivocally whether or not the ring buffer is in that mode (although
|
||||
absolute timestamps can coexist with the normal delta timestamps, when
|
||||
the ring buffer is in absolute mode, timestamps written while absolute
|
||||
mode is in effect take up more space in the buffer, and are not as
|
||||
efficient).
|
||||
|
||||
Link: http://lkml.kernel.org/r/e8aa7b1cde1cf15014e66545d06ac6ef2ebba456.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 0eba34f9bf5b66217355a6a66054b3194aca123d)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
Documentation/trace/ftrace.txt | 24 +++++++++++++++++
|
||||
kernel/trace/trace.c | 47 ++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 71 insertions(+)
|
||||
|
||||
diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt
|
||||
index d4601df6e72e..54213e5c23f6 100644
|
||||
--- a/Documentation/trace/ftrace.txt
|
||||
+++ b/Documentation/trace/ftrace.txt
|
||||
@@ -539,6 +539,30 @@ of ftrace. Here is a list of some of the key files:
|
||||
|
||||
See events.txt for more information.
|
||||
|
||||
+ timestamp_mode:
|
||||
+
|
||||
+ Certain tracers may change the timestamp mode used when
|
||||
+ logging trace events into the event buffer. Events with
|
||||
+ different modes can coexist within a buffer but the mode in
|
||||
+ effect when an event is logged determines which timestamp mode
|
||||
+ is used for that event. The default timestamp mode is
|
||||
+ 'delta'.
|
||||
+
|
||||
+ Usual timestamp modes for tracing:
|
||||
+
|
||||
+ # cat timestamp_mode
|
||||
+ [delta] absolute
|
||||
+
|
||||
+ The timestamp mode with the square brackets around it is the
|
||||
+ one in effect.
|
||||
+
|
||||
+ delta: Default timestamp mode - timestamp is a delta against
|
||||
+ a per-buffer timestamp.
|
||||
+
|
||||
+ absolute: The timestamp is a full timestamp, not a delta
|
||||
+ against some other value. As such it takes up more
|
||||
+ space and is less efficient.
|
||||
+
|
||||
hwlat_detector:
|
||||
|
||||
Directory for the Hardware Latency Detector.
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index ffb67272b642..c358cae7461f 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -4531,6 +4531,9 @@ static const char readme_msg[] =
|
||||
#ifdef CONFIG_X86_64
|
||||
" x86-tsc: TSC cycle counter\n"
|
||||
#endif
|
||||
+ "\n timestamp_mode\t-view the mode used to timestamp events\n"
|
||||
+ " delta: Delta difference against a buffer-wide timestamp\n"
|
||||
+ " absolute: Absolute (standalone) timestamp\n"
|
||||
"\n trace_marker\t\t- Writes into this file writes into the kernel buffer\n"
|
||||
"\n trace_marker_raw\t\t- Writes into this file writes binary data into the kernel buffer\n"
|
||||
" tracing_cpumask\t- Limit which CPUs to trace\n"
|
||||
@@ -6298,6 +6301,40 @@ static int tracing_clock_open(struct inode *inode, struct file *file)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int tracing_time_stamp_mode_show(struct seq_file *m, void *v)
|
||||
+{
|
||||
+ struct trace_array *tr = m->private;
|
||||
+
|
||||
+ mutex_lock(&trace_types_lock);
|
||||
+
|
||||
+ if (ring_buffer_time_stamp_abs(tr->trace_buffer.buffer))
|
||||
+ seq_puts(m, "delta [absolute]\n");
|
||||
+ else
|
||||
+ seq_puts(m, "[delta] absolute\n");
|
||||
+
|
||||
+ mutex_unlock(&trace_types_lock);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int tracing_time_stamp_mode_open(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct trace_array *tr = inode->i_private;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (tracing_disabled)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (trace_array_get(tr))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ ret = single_open(file, tracing_time_stamp_mode_show, inode->i_private);
|
||||
+ if (ret < 0)
|
||||
+ trace_array_put(tr);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -6576,6 +6613,13 @@ static const struct file_operations trace_clock_fops = {
|
||||
.write = tracing_clock_write,
|
||||
};
|
||||
|
||||
+static const struct file_operations trace_time_stamp_mode_fops = {
|
||||
+ .open = tracing_time_stamp_mode_open,
|
||||
+ .read = seq_read,
|
||||
+ .llseek = seq_lseek,
|
||||
+ .release = tracing_single_release_tr,
|
||||
+};
|
||||
+
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
static const struct file_operations snapshot_fops = {
|
||||
.open = tracing_snapshot_open,
|
||||
@@ -7900,6 +7944,9 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
|
||||
trace_create_file("tracing_on", 0644, d_tracer,
|
||||
tr, &rb_simple_fops);
|
||||
|
||||
+ trace_create_file("timestamp_mode", 0444, d_tracer, tr,
|
||||
+ &trace_time_stamp_mode_fops);
|
||||
+
|
||||
create_trace_options_dir(tr);
|
||||
|
||||
#if defined(CONFIG_TRACER_MAX_TRACE) || defined(CONFIG_HWLAT_TRACER)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
From 635d753e923c3acd4de41b201ccc450da32e8330 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:42 -0600
|
||||
Subject: [PATCH 085/450] tracing: Give event triggers access to
|
||||
ring_buffer_event
|
||||
|
||||
The ring_buffer event can provide a timestamp that may be useful to
|
||||
various triggers - pass it into the handlers for that purpose.
|
||||
|
||||
Link: http://lkml.kernel.org/r/6de592683b59fa70ffa5d43d0109896623fc1367.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 373514437a6f75b5cfe890742b590f2c12f6c335)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
include/linux/trace_events.h | 14 +++++----
|
||||
kernel/trace/trace.h | 9 +++---
|
||||
kernel/trace/trace_events_hist.c | 11 ++++---
|
||||
kernel/trace/trace_events_trigger.c | 47 ++++++++++++++++++-----------
|
||||
4 files changed, 49 insertions(+), 32 deletions(-)
|
||||
|
||||
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
|
||||
index 2bcb4dc6df1a..aefc80f2909b 100644
|
||||
--- a/include/linux/trace_events.h
|
||||
+++ b/include/linux/trace_events.h
|
||||
@@ -402,11 +402,13 @@ enum event_trigger_type {
|
||||
|
||||
extern int filter_match_preds(struct event_filter *filter, void *rec);
|
||||
|
||||
-extern enum event_trigger_type event_triggers_call(struct trace_event_file *file,
|
||||
- void *rec);
|
||||
-extern void event_triggers_post_call(struct trace_event_file *file,
|
||||
- enum event_trigger_type tt,
|
||||
- void *rec);
|
||||
+extern enum event_trigger_type
|
||||
+event_triggers_call(struct trace_event_file *file, void *rec,
|
||||
+ struct ring_buffer_event *event);
|
||||
+extern void
|
||||
+event_triggers_post_call(struct trace_event_file *file,
|
||||
+ enum event_trigger_type tt,
|
||||
+ void *rec, struct ring_buffer_event *event);
|
||||
|
||||
bool trace_event_ignore_this_pid(struct trace_event_file *trace_file);
|
||||
|
||||
@@ -426,7 +428,7 @@ trace_trigger_soft_disabled(struct trace_event_file *file)
|
||||
|
||||
if (!(eflags & EVENT_FILE_FL_TRIGGER_COND)) {
|
||||
if (eflags & EVENT_FILE_FL_TRIGGER_MODE)
|
||||
- event_triggers_call(file, NULL);
|
||||
+ event_triggers_call(file, NULL, NULL);
|
||||
if (eflags & EVENT_FILE_FL_SOFT_DISABLED)
|
||||
return true;
|
||||
if (eflags & EVENT_FILE_FL_PID_FILTER)
|
||||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
|
||||
index 0506c4cdfeba..e2e12710d53c 100644
|
||||
--- a/kernel/trace/trace.h
|
||||
+++ b/kernel/trace/trace.h
|
||||
@@ -1296,7 +1296,7 @@ __event_trigger_test_discard(struct trace_event_file *file,
|
||||
unsigned long eflags = file->flags;
|
||||
|
||||
if (eflags & EVENT_FILE_FL_TRIGGER_COND)
|
||||
- *tt = event_triggers_call(file, entry);
|
||||
+ *tt = event_triggers_call(file, entry, event);
|
||||
|
||||
if (test_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags) ||
|
||||
(unlikely(file->flags & EVENT_FILE_FL_FILTERED) &&
|
||||
@@ -1333,7 +1333,7 @@ event_trigger_unlock_commit(struct trace_event_file *file,
|
||||
trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc);
|
||||
|
||||
if (tt)
|
||||
- event_triggers_post_call(file, tt, entry);
|
||||
+ event_triggers_post_call(file, tt, entry, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1366,7 +1366,7 @@ event_trigger_unlock_commit_regs(struct trace_event_file *file,
|
||||
irq_flags, pc, regs);
|
||||
|
||||
if (tt)
|
||||
- event_triggers_post_call(file, tt, entry);
|
||||
+ event_triggers_post_call(file, tt, entry, event);
|
||||
}
|
||||
|
||||
#define FILTER_PRED_INVALID ((unsigned short)-1)
|
||||
@@ -1591,7 +1591,8 @@ extern int register_trigger_hist_enable_disable_cmds(void);
|
||||
*/
|
||||
struct event_trigger_ops {
|
||||
void (*func)(struct event_trigger_data *data,
|
||||
- void *rec);
|
||||
+ void *rec,
|
||||
+ struct ring_buffer_event *rbe);
|
||||
int (*init)(struct event_trigger_ops *ops,
|
||||
struct event_trigger_data *data);
|
||||
void (*free)(struct event_trigger_ops *ops,
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index c57e5369b0c2..77079c79b6d4 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -909,7 +909,8 @@ static inline void add_to_key(char *compound_key, void *key,
|
||||
memcpy(compound_key + key_field->offset, key, size);
|
||||
}
|
||||
|
||||
-static void event_hist_trigger(struct event_trigger_data *data, void *rec)
|
||||
+static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct hist_trigger_data *hist_data = data->private_data;
|
||||
bool use_compound_key = (hist_data->n_keys > 1);
|
||||
@@ -1660,7 +1661,8 @@ __init int register_trigger_hist_cmd(void)
|
||||
}
|
||||
|
||||
static void
|
||||
-hist_enable_trigger(struct event_trigger_data *data, void *rec)
|
||||
+hist_enable_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct enable_trigger_data *enable_data = data->private_data;
|
||||
struct event_trigger_data *test;
|
||||
@@ -1676,7 +1678,8 @@ hist_enable_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-hist_enable_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+hist_enable_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (!data->count)
|
||||
return;
|
||||
@@ -1684,7 +1687,7 @@ hist_enable_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
if (data->count != -1)
|
||||
(data->count)--;
|
||||
|
||||
- hist_enable_trigger(data, rec);
|
||||
+ hist_enable_trigger(data, rec, event);
|
||||
}
|
||||
|
||||
static struct event_trigger_ops hist_enable_trigger_ops = {
|
||||
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
|
||||
index 43254c5e7e16..4c269f2e00a4 100644
|
||||
--- a/kernel/trace/trace_events_trigger.c
|
||||
+++ b/kernel/trace/trace_events_trigger.c
|
||||
@@ -63,7 +63,8 @@ void trigger_data_free(struct event_trigger_data *data)
|
||||
* any trigger that should be deferred, ETT_NONE if nothing to defer.
|
||||
*/
|
||||
enum event_trigger_type
|
||||
-event_triggers_call(struct trace_event_file *file, void *rec)
|
||||
+event_triggers_call(struct trace_event_file *file, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct event_trigger_data *data;
|
||||
enum event_trigger_type tt = ETT_NONE;
|
||||
@@ -76,7 +77,7 @@ event_triggers_call(struct trace_event_file *file, void *rec)
|
||||
if (data->paused)
|
||||
continue;
|
||||
if (!rec) {
|
||||
- data->ops->func(data, rec);
|
||||
+ data->ops->func(data, rec, event);
|
||||
continue;
|
||||
}
|
||||
filter = rcu_dereference_sched(data->filter);
|
||||
@@ -86,7 +87,7 @@ event_triggers_call(struct trace_event_file *file, void *rec)
|
||||
tt |= data->cmd_ops->trigger_type;
|
||||
continue;
|
||||
}
|
||||
- data->ops->func(data, rec);
|
||||
+ data->ops->func(data, rec, event);
|
||||
}
|
||||
return tt;
|
||||
}
|
||||
@@ -108,7 +109,7 @@ EXPORT_SYMBOL_GPL(event_triggers_call);
|
||||
void
|
||||
event_triggers_post_call(struct trace_event_file *file,
|
||||
enum event_trigger_type tt,
|
||||
- void *rec)
|
||||
+ void *rec, struct ring_buffer_event *event)
|
||||
{
|
||||
struct event_trigger_data *data;
|
||||
|
||||
@@ -116,7 +117,7 @@ event_triggers_post_call(struct trace_event_file *file,
|
||||
if (data->paused)
|
||||
continue;
|
||||
if (data->cmd_ops->trigger_type & tt)
|
||||
- data->ops->func(data, rec);
|
||||
+ data->ops->func(data, rec, event);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(event_triggers_post_call);
|
||||
@@ -915,7 +916,8 @@ void set_named_trigger_data(struct event_trigger_data *data,
|
||||
}
|
||||
|
||||
static void
|
||||
-traceon_trigger(struct event_trigger_data *data, void *rec)
|
||||
+traceon_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (tracing_is_on())
|
||||
return;
|
||||
@@ -924,7 +926,8 @@ traceon_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-traceon_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+traceon_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (tracing_is_on())
|
||||
return;
|
||||
@@ -939,7 +942,8 @@ traceon_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-traceoff_trigger(struct event_trigger_data *data, void *rec)
|
||||
+traceoff_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (!tracing_is_on())
|
||||
return;
|
||||
@@ -948,7 +952,8 @@ traceoff_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-traceoff_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+traceoff_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (!tracing_is_on())
|
||||
return;
|
||||
@@ -1045,7 +1050,8 @@ static struct event_command trigger_traceoff_cmd = {
|
||||
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
static void
|
||||
-snapshot_trigger(struct event_trigger_data *data, void *rec)
|
||||
+snapshot_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct trace_event_file *file = data->private_data;
|
||||
|
||||
@@ -1056,7 +1062,8 @@ snapshot_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-snapshot_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+snapshot_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (!data->count)
|
||||
return;
|
||||
@@ -1064,7 +1071,7 @@ snapshot_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
if (data->count != -1)
|
||||
(data->count)--;
|
||||
|
||||
- snapshot_trigger(data, rec);
|
||||
+ snapshot_trigger(data, rec, event);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1143,13 +1150,15 @@ static __init int register_trigger_snapshot_cmd(void) { return 0; }
|
||||
#define STACK_SKIP 3
|
||||
|
||||
static void
|
||||
-stacktrace_trigger(struct event_trigger_data *data, void *rec)
|
||||
+stacktrace_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
trace_dump_stack(STACK_SKIP);
|
||||
}
|
||||
|
||||
static void
|
||||
-stacktrace_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+stacktrace_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
if (!data->count)
|
||||
return;
|
||||
@@ -1157,7 +1166,7 @@ stacktrace_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
if (data->count != -1)
|
||||
(data->count)--;
|
||||
|
||||
- stacktrace_trigger(data, rec);
|
||||
+ stacktrace_trigger(data, rec, event);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1219,7 +1228,8 @@ static __init void unregister_trigger_traceon_traceoff_cmds(void)
|
||||
}
|
||||
|
||||
static void
|
||||
-event_enable_trigger(struct event_trigger_data *data, void *rec)
|
||||
+event_enable_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct enable_trigger_data *enable_data = data->private_data;
|
||||
|
||||
@@ -1230,7 +1240,8 @@ event_enable_trigger(struct event_trigger_data *data, void *rec)
|
||||
}
|
||||
|
||||
static void
|
||||
-event_enable_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
+event_enable_count_trigger(struct event_trigger_data *data, void *rec,
|
||||
+ struct ring_buffer_event *event)
|
||||
{
|
||||
struct enable_trigger_data *enable_data = data->private_data;
|
||||
|
||||
@@ -1244,7 +1255,7 @@ event_enable_count_trigger(struct event_trigger_data *data, void *rec)
|
||||
if (data->count != -1)
|
||||
(data->count)--;
|
||||
|
||||
- event_enable_trigger(data, rec);
|
||||
+ event_enable_trigger(data, rec, event);
|
||||
}
|
||||
|
||||
int event_enable_trigger_print(struct seq_file *m,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
From 9bd98ebd9a345d81a789160ce7ab7a0c4be5c61a Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:43 -0600
|
||||
Subject: [PATCH 086/450] tracing: Add ring buffer event param to hist field
|
||||
functions
|
||||
|
||||
Some events such as timestamps require access to a ring_buffer_event
|
||||
struct; add a param so that hist field functions can access that.
|
||||
|
||||
Link: http://lkml.kernel.org/r/2ff4af18e72b6002eb86b26b2a7f39cef7d1dfe4.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit df7253a730d0aaef760d45ea234dc087ba7cac88)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 39 ++++++++++++++++++++------------
|
||||
1 file changed, 24 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 77079c79b6d4..9126be52ff4d 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
struct hist_field;
|
||||
|
||||
-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event);
|
||||
+typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
|
||||
+ struct ring_buffer_event *rbe);
|
||||
|
||||
#define HIST_FIELD_OPERANDS_MAX 2
|
||||
|
||||
@@ -40,24 +41,28 @@ struct hist_field {
|
||||
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
};
|
||||
|
||||
-static u64 hist_field_none(struct hist_field *field, void *event)
|
||||
+static u64 hist_field_none(struct hist_field *field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static u64 hist_field_counter(struct hist_field *field, void *event)
|
||||
+static u64 hist_field_counter(struct hist_field *field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
-static u64 hist_field_string(struct hist_field *hist_field, void *event)
|
||||
+static u64 hist_field_string(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
char *addr = (char *)(event + hist_field->field->offset);
|
||||
|
||||
return (u64)(unsigned long)addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_dynstring(struct hist_field *hist_field, void *event)
|
||||
+static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
u32 str_item = *(u32 *)(event + hist_field->field->offset);
|
||||
int str_loc = str_item & 0xffff;
|
||||
@@ -66,24 +71,28 @@ static u64 hist_field_dynstring(struct hist_field *hist_field, void *event)
|
||||
return (u64)(unsigned long)addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_pstring(struct hist_field *hist_field, void *event)
|
||||
+static u64 hist_field_pstring(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
char **addr = (char **)(event + hist_field->field->offset);
|
||||
|
||||
return (u64)(unsigned long)*addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_log2(struct hist_field *hist_field, void *event)
|
||||
+static u64 hist_field_log2(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
struct hist_field *operand = hist_field->operands[0];
|
||||
|
||||
- u64 val = operand->fn(operand, event);
|
||||
+ u64 val = operand->fn(operand, event, rbe);
|
||||
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
|
||||
#define DEFINE_HIST_FIELD_FN(type) \
|
||||
-static u64 hist_field_##type(struct hist_field *hist_field, void *event)\
|
||||
+ static u64 hist_field_##type(struct hist_field *hist_field, \
|
||||
+ void *event, \
|
||||
+ struct ring_buffer_event *rbe) \
|
||||
{ \
|
||||
type *addr = (type *)(event + hist_field->field->offset); \
|
||||
\
|
||||
@@ -871,8 +880,8 @@ create_hist_data(unsigned int map_bits,
|
||||
}
|
||||
|
||||
static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
- struct tracing_map_elt *elt,
|
||||
- void *rec)
|
||||
+ struct tracing_map_elt *elt, void *rec,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
struct hist_field *hist_field;
|
||||
unsigned int i;
|
||||
@@ -880,7 +889,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
|
||||
for_each_hist_val_field(i, hist_data) {
|
||||
hist_field = hist_data->fields[i];
|
||||
- hist_val = hist_field->fn(hist_field, rec);
|
||||
+ hist_val = hist_field->fn(hist_field, rec, rbe);
|
||||
tracing_map_update_sum(elt, i, hist_val);
|
||||
}
|
||||
}
|
||||
@@ -910,7 +919,7 @@ static inline void add_to_key(char *compound_key, void *key,
|
||||
}
|
||||
|
||||
static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
- struct ring_buffer_event *event)
|
||||
+ struct ring_buffer_event *rbe)
|
||||
{
|
||||
struct hist_trigger_data *hist_data = data->private_data;
|
||||
bool use_compound_key = (hist_data->n_keys > 1);
|
||||
@@ -939,7 +948,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
|
||||
key = entries;
|
||||
} else {
|
||||
- field_contents = key_field->fn(key_field, rec);
|
||||
+ field_contents = key_field->fn(key_field, rec, rbe);
|
||||
if (key_field->flags & HIST_FIELD_FL_STRING) {
|
||||
key = (void *)(unsigned long)field_contents;
|
||||
use_compound_key = true;
|
||||
@@ -956,7 +965,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
|
||||
elt = tracing_map_insert(hist_data->map, key);
|
||||
if (elt)
|
||||
- hist_trigger_elt_update(hist_data, elt, rec);
|
||||
+ hist_trigger_elt_update(hist_data, elt, rec, rbe);
|
||||
}
|
||||
|
||||
static void hist_trigger_stacktrace_print(struct seq_file *m,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
From e2bbd29cab8010be36eb88dc22c5e3addc9d7a2c Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:44 -0600
|
||||
Subject: [PATCH 087/450] tracing: Break out hist trigger assignment parsing
|
||||
|
||||
This will make it easier to add variables, and makes the parsing code
|
||||
cleaner regardless.
|
||||
|
||||
Link: http://lkml.kernel.org/r/e574b3291bbe15e35a4dfc87e5395aa715701c98.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Rajvi Jingar <rajvi.jingar@intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 3c1e23def1291b21a2057f883ccc0456418dc5ad)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 72 ++++++++++++++++++++++----------
|
||||
1 file changed, 51 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 9126be52ff4d..4935290fd1e8 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -251,6 +251,51 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
|
||||
kfree(attrs);
|
||||
}
|
||||
|
||||
+static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if ((strncmp(str, "key=", strlen("key=")) == 0) ||
|
||||
+ (strncmp(str, "keys=", strlen("keys=")) == 0)) {
|
||||
+ attrs->keys_str = kstrdup(str, GFP_KERNEL);
|
||||
+ if (!attrs->keys_str) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ } else if ((strncmp(str, "val=", strlen("val=")) == 0) ||
|
||||
+ (strncmp(str, "vals=", strlen("vals=")) == 0) ||
|
||||
+ (strncmp(str, "values=", strlen("values=")) == 0)) {
|
||||
+ attrs->vals_str = kstrdup(str, GFP_KERNEL);
|
||||
+ if (!attrs->vals_str) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ } else if (strncmp(str, "sort=", strlen("sort=")) == 0) {
|
||||
+ attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
|
||||
+ if (!attrs->sort_key_str) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ } else if (strncmp(str, "name=", strlen("name=")) == 0) {
|
||||
+ attrs->name = kstrdup(str, GFP_KERNEL);
|
||||
+ if (!attrs->name) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ } else if (strncmp(str, "size=", strlen("size=")) == 0) {
|
||||
+ int map_bits = parse_map_size(str);
|
||||
+
|
||||
+ if (map_bits < 0) {
|
||||
+ ret = map_bits;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ attrs->map_bits = map_bits;
|
||||
+ } else
|
||||
+ ret = -EINVAL;
|
||||
+ out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
|
||||
{
|
||||
struct hist_trigger_attrs *attrs;
|
||||
@@ -263,33 +308,18 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
|
||||
while (trigger_str) {
|
||||
char *str = strsep(&trigger_str, ":");
|
||||
|
||||
- if ((strncmp(str, "key=", strlen("key=")) == 0) ||
|
||||
- (strncmp(str, "keys=", strlen("keys=")) == 0))
|
||||
- attrs->keys_str = kstrdup(str, GFP_KERNEL);
|
||||
- else if ((strncmp(str, "val=", strlen("val=")) == 0) ||
|
||||
- (strncmp(str, "vals=", strlen("vals=")) == 0) ||
|
||||
- (strncmp(str, "values=", strlen("values=")) == 0))
|
||||
- attrs->vals_str = kstrdup(str, GFP_KERNEL);
|
||||
- else if (strncmp(str, "sort=", strlen("sort=")) == 0)
|
||||
- attrs->sort_key_str = kstrdup(str, GFP_KERNEL);
|
||||
- else if (strncmp(str, "name=", strlen("name=")) == 0)
|
||||
- attrs->name = kstrdup(str, GFP_KERNEL);
|
||||
- else if (strcmp(str, "pause") == 0)
|
||||
+ if (strchr(str, '=')) {
|
||||
+ ret = parse_assignment(str, attrs);
|
||||
+ if (ret)
|
||||
+ goto free;
|
||||
+ } else if (strcmp(str, "pause") == 0)
|
||||
attrs->pause = true;
|
||||
else if ((strcmp(str, "cont") == 0) ||
|
||||
(strcmp(str, "continue") == 0))
|
||||
attrs->cont = true;
|
||||
else if (strcmp(str, "clear") == 0)
|
||||
attrs->clear = true;
|
||||
- else if (strncmp(str, "size=", strlen("size=")) == 0) {
|
||||
- int map_bits = parse_map_size(str);
|
||||
-
|
||||
- if (map_bits < 0) {
|
||||
- ret = map_bits;
|
||||
- goto free;
|
||||
- }
|
||||
- attrs->map_bits = map_bits;
|
||||
- } else {
|
||||
+ else {
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
From 67b7fdf52c9a5092aea0a1dd29becd8b5ba7292b Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:45 -0600
|
||||
Subject: [PATCH 088/450] tracing: Add hist trigger timestamp support
|
||||
|
||||
Add support for a timestamp event field. This is actually a 'pseudo-'
|
||||
event field in that it behaves like it's part of the event record, but
|
||||
is really part of the corresponding ring buffer event.
|
||||
|
||||
To make use of the timestamp field, users can specify
|
||||
"common_timestamp" as a field name for any histogram. Note that this
|
||||
doesn't make much sense on its own either as either a key or value,
|
||||
but needs to be supported even so, since follow-on patches will add
|
||||
support for making use of this field in time deltas. The
|
||||
common_timestamp 'field' is not a bona fide event field - so you won't
|
||||
find it in the event description - but rather it's a synthetic field
|
||||
that can be used like a real field.
|
||||
|
||||
Note that the use of this field requires the ring buffer be put into
|
||||
'absolute timestamp' mode, which saves the complete timestamp for each
|
||||
event rather than an offset. This mode will be enabled if and only if
|
||||
a histogram makes use of the "common_timestamp" field.
|
||||
|
||||
Link: http://lkml.kernel.org/r/97afbd646ed146e26271f3458b4b33e16d7817c2.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Baohong Liu <baohong.liu@intel.com>
|
||||
[kasan use-after-free fix]
|
||||
Signed-off-by: Vedang Patel <vedang.patel@intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 5d9d58b00ff82078deac8557c91359cd13c8959d)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 94 ++++++++++++++++++++++++--------
|
||||
1 file changed, 71 insertions(+), 23 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 4935290fd1e8..cd80bca34cec 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -89,6 +89,12 @@ static u64 hist_field_log2(struct hist_field *hist_field, void *event,
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
|
||||
+static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
+{
|
||||
+ return ring_buffer_event_time_stamp(rbe);
|
||||
+}
|
||||
+
|
||||
#define DEFINE_HIST_FIELD_FN(type) \
|
||||
static u64 hist_field_##type(struct hist_field *hist_field, \
|
||||
void *event, \
|
||||
@@ -135,6 +141,7 @@ enum hist_field_flags {
|
||||
HIST_FIELD_FL_SYSCALL = 1 << 7,
|
||||
HIST_FIELD_FL_STACKTRACE = 1 << 8,
|
||||
HIST_FIELD_FL_LOG2 = 1 << 9,
|
||||
+ HIST_FIELD_FL_TIMESTAMP = 1 << 10,
|
||||
};
|
||||
|
||||
struct hist_trigger_attrs {
|
||||
@@ -159,6 +166,7 @@ struct hist_trigger_data {
|
||||
struct trace_event_file *event_file;
|
||||
struct hist_trigger_attrs *attrs;
|
||||
struct tracing_map *map;
|
||||
+ bool enable_timestamps;
|
||||
};
|
||||
|
||||
static const char *hist_field_name(struct hist_field *field,
|
||||
@@ -173,6 +181,8 @@ static const char *hist_field_name(struct hist_field *field,
|
||||
field_name = field->field->name;
|
||||
else if (field->flags & HIST_FIELD_FL_LOG2)
|
||||
field_name = hist_field_name(field->operands[0], ++level);
|
||||
+ else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
+ field_name = "common_timestamp";
|
||||
|
||||
if (field_name == NULL)
|
||||
field_name = "";
|
||||
@@ -440,6 +450,12 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (flags & HIST_FIELD_FL_TIMESTAMP) {
|
||||
+ hist_field->fn = hist_field_timestamp;
|
||||
+ hist_field->size = sizeof(u64);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
if (WARN_ON_ONCE(!field))
|
||||
goto out;
|
||||
|
||||
@@ -517,10 +533,15 @@ static int create_val_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- field = trace_find_event_field(file->event_call, field_name);
|
||||
- if (!field || !field->size) {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
+ if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
+ flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
+ hist_data->enable_timestamps = true;
|
||||
+ } else {
|
||||
+ field = trace_find_event_field(file->event_call, field_name);
|
||||
+ if (!field || !field->size) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
}
|
||||
|
||||
hist_data->fields[val_idx] = create_hist_field(field, flags);
|
||||
@@ -615,16 +636,22 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- field = trace_find_event_field(file->event_call, field_name);
|
||||
- if (!field || !field->size) {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
+ if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
+ flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
+ hist_data->enable_timestamps = true;
|
||||
+ key_size = sizeof(u64);
|
||||
+ } else {
|
||||
+ field = trace_find_event_field(file->event_call, field_name);
|
||||
+ if (!field || !field->size) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
|
||||
- if (is_string_field(field))
|
||||
- key_size = MAX_FILTER_STR_VAL;
|
||||
- else
|
||||
- key_size = field->size;
|
||||
+ if (is_string_field(field))
|
||||
+ key_size = MAX_FILTER_STR_VAL;
|
||||
+ else
|
||||
+ key_size = field->size;
|
||||
+ }
|
||||
}
|
||||
|
||||
hist_data->fields[key_idx] = create_hist_field(field, flags);
|
||||
@@ -820,6 +847,9 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
|
||||
|
||||
if (hist_field->flags & HIST_FIELD_FL_STACKTRACE)
|
||||
cmp_fn = tracing_map_cmp_none;
|
||||
+ else if (!field)
|
||||
+ cmp_fn = tracing_map_cmp_num(hist_field->size,
|
||||
+ hist_field->is_signed);
|
||||
else if (is_string_field(field))
|
||||
cmp_fn = tracing_map_cmp_string;
|
||||
else
|
||||
@@ -1217,7 +1247,11 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
||||
{
|
||||
const char *field_name = hist_field_name(hist_field, 0);
|
||||
|
||||
- seq_printf(m, "%s", field_name);
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
+ seq_puts(m, "common_timestamp");
|
||||
+ else if (field_name)
|
||||
+ seq_printf(m, "%s", field_name);
|
||||
+
|
||||
if (hist_field->flags) {
|
||||
const char *flags_str = get_hist_field_flags(hist_field);
|
||||
|
||||
@@ -1268,27 +1302,25 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
|
||||
for (i = 0; i < hist_data->n_sort_keys; i++) {
|
||||
struct tracing_map_sort_key *sort_key;
|
||||
+ unsigned int idx;
|
||||
|
||||
sort_key = &hist_data->sort_keys[i];
|
||||
+ idx = sort_key->field_idx;
|
||||
+
|
||||
+ if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX))
|
||||
+ return -EINVAL;
|
||||
|
||||
if (i > 0)
|
||||
seq_puts(m, ",");
|
||||
|
||||
- if (sort_key->field_idx == HITCOUNT_IDX)
|
||||
+ if (idx == HITCOUNT_IDX)
|
||||
seq_puts(m, "hitcount");
|
||||
- else {
|
||||
- unsigned int idx = sort_key->field_idx;
|
||||
-
|
||||
- if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX))
|
||||
- return -EINVAL;
|
||||
-
|
||||
+ else
|
||||
hist_field_print(m, hist_data->fields[idx]);
|
||||
- }
|
||||
|
||||
if (sort_key->descending)
|
||||
seq_puts(m, ".descending");
|
||||
}
|
||||
-
|
||||
seq_printf(m, ":size=%u", (1 << hist_data->map->map_bits));
|
||||
|
||||
if (data->filter_str)
|
||||
@@ -1456,6 +1488,10 @@ static bool hist_trigger_match(struct event_trigger_data *data,
|
||||
return false;
|
||||
if (key_field->offset != key_field_test->offset)
|
||||
return false;
|
||||
+ if (key_field->size != key_field_test->size)
|
||||
+ return false;
|
||||
+ if (key_field->is_signed != key_field_test->is_signed)
|
||||
+ return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < hist_data->n_sort_keys; i++) {
|
||||
@@ -1538,6 +1574,9 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
|
||||
update_cond_flag(file);
|
||||
|
||||
+ if (hist_data->enable_timestamps)
|
||||
+ tracing_set_time_stamp_abs(file->tr, true);
|
||||
+
|
||||
if (trace_event_trigger_enable_disable(file, 1) < 0) {
|
||||
list_del_rcu(&data->list);
|
||||
update_cond_flag(file);
|
||||
@@ -1572,17 +1611,26 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
|
||||
if (unregistered && test->ops->free)
|
||||
test->ops->free(test->ops, test);
|
||||
+
|
||||
+ if (hist_data->enable_timestamps) {
|
||||
+ if (unregistered)
|
||||
+ tracing_set_time_stamp_abs(file->tr, false);
|
||||
+ }
|
||||
}
|
||||
|
||||
static void hist_unreg_all(struct trace_event_file *file)
|
||||
{
|
||||
struct event_trigger_data *test, *n;
|
||||
+ struct hist_trigger_data *hist_data;
|
||||
|
||||
list_for_each_entry_safe(test, n, &file->triggers, list) {
|
||||
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
+ hist_data = test->private_data;
|
||||
list_del_rcu(&test->list);
|
||||
trace_event_trigger_enable_disable(file, 0);
|
||||
update_cond_flag(file);
|
||||
+ if (hist_data->enable_timestamps)
|
||||
+ tracing_set_time_stamp_abs(file->tr, false);
|
||||
if (test->ops->free)
|
||||
test->ops->free(test->ops, test);
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
From 3c95c215d087d86eb91bda208a6b219eadb556e6 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:46 -0600
|
||||
Subject: [PATCH 089/450] tracing: Add per-element variable support to
|
||||
tracing_map
|
||||
|
||||
In order to allow information to be passed between trace events, add
|
||||
support for per-element variables to tracing_map. This provides a
|
||||
means for histograms to associate a value or values with an entry when
|
||||
it's saved or updated, and retrieved by a subsequent event occurrences.
|
||||
|
||||
Variables can be set using tracing_map_set_var() and read using
|
||||
tracing_map_read_var(). tracing_map_var_set() returns true or false
|
||||
depending on whether or not the variable has been set or not, which is
|
||||
important for event-matching applications.
|
||||
|
||||
tracing_map_read_var_once() reads the variable and resets it to the
|
||||
'unset' state, implementing read-once variables, which are also
|
||||
important for event-matching uses.
|
||||
|
||||
Link: http://lkml.kernel.org/r/7fa001108252556f0c6dd9d63145eabfe3370d1a.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 42a38132f9e154e1fa2dd2182dff17f9c0e7ee7e)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/tracing_map.c | 108 +++++++++++++++++++++++++++++++++++++
|
||||
kernel/trace/tracing_map.h | 11 ++++
|
||||
2 files changed, 119 insertions(+)
|
||||
|
||||
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
|
||||
index f47a4d54bcf0..5cadb1b8b5fe 100644
|
||||
--- a/kernel/trace/tracing_map.c
|
||||
+++ b/kernel/trace/tracing_map.c
|
||||
@@ -66,6 +66,73 @@ u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i)
|
||||
return (u64)atomic64_read(&elt->fields[i].sum);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * tracing_map_set_var - Assign a tracing_map_elt's variable field
|
||||
+ * @elt: The tracing_map_elt
|
||||
+ * @i: The index of the given variable associated with the tracing_map_elt
|
||||
+ * @n: The value to assign
|
||||
+ *
|
||||
+ * Assign n to variable i associated with the specified tracing_map_elt
|
||||
+ * instance. The index i is the index returned by the call to
|
||||
+ * tracing_map_add_var() when the tracing map was set up.
|
||||
+ */
|
||||
+void tracing_map_set_var(struct tracing_map_elt *elt, unsigned int i, u64 n)
|
||||
+{
|
||||
+ atomic64_set(&elt->vars[i], n);
|
||||
+ elt->var_set[i] = true;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * tracing_map_var_set - Return whether or not a variable has been set
|
||||
+ * @elt: The tracing_map_elt
|
||||
+ * @i: The index of the given variable associated with the tracing_map_elt
|
||||
+ *
|
||||
+ * Return true if the variable has been set, false otherwise. The
|
||||
+ * index i is the index returned by the call to tracing_map_add_var()
|
||||
+ * when the tracing map was set up.
|
||||
+ */
|
||||
+bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i)
|
||||
+{
|
||||
+ return elt->var_set[i];
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * tracing_map_read_var - Return the value of a tracing_map_elt's variable field
|
||||
+ * @elt: The tracing_map_elt
|
||||
+ * @i: The index of the given variable associated with the tracing_map_elt
|
||||
+ *
|
||||
+ * Retrieve the value of the variable i associated with the specified
|
||||
+ * tracing_map_elt instance. The index i is the index returned by the
|
||||
+ * call to tracing_map_add_var() when the tracing map was set
|
||||
+ * up.
|
||||
+ *
|
||||
+ * Return: The variable value associated with field i for elt.
|
||||
+ */
|
||||
+u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i)
|
||||
+{
|
||||
+ return (u64)atomic64_read(&elt->vars[i]);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * tracing_map_read_var_once - Return and reset a tracing_map_elt's variable field
|
||||
+ * @elt: The tracing_map_elt
|
||||
+ * @i: The index of the given variable associated with the tracing_map_elt
|
||||
+ *
|
||||
+ * Retrieve the value of the variable i associated with the specified
|
||||
+ * tracing_map_elt instance, and reset the variable to the 'not set'
|
||||
+ * state. The index i is the index returned by the call to
|
||||
+ * tracing_map_add_var() when the tracing map was set up. The reset
|
||||
+ * essentially makes the variable a read-once variable if it's only
|
||||
+ * accessed using this function.
|
||||
+ *
|
||||
+ * Return: The variable value associated with field i for elt.
|
||||
+ */
|
||||
+u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i)
|
||||
+{
|
||||
+ elt->var_set[i] = false;
|
||||
+ return (u64)atomic64_read(&elt->vars[i]);
|
||||
+}
|
||||
+
|
||||
int tracing_map_cmp_string(void *val_a, void *val_b)
|
||||
{
|
||||
char *a = val_a;
|
||||
@@ -170,6 +237,28 @@ int tracing_map_add_sum_field(struct tracing_map *map)
|
||||
return tracing_map_add_field(map, tracing_map_cmp_atomic64);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * tracing_map_add_var - Add a field describing a tracing_map var
|
||||
+ * @map: The tracing_map
|
||||
+ *
|
||||
+ * Add a var to the map and return the index identifying it in the map
|
||||
+ * and associated tracing_map_elts. This is the index used for
|
||||
+ * instance to update a var for a particular tracing_map_elt using
|
||||
+ * tracing_map_update_var() or reading it via tracing_map_read_var().
|
||||
+ *
|
||||
+ * Return: The index identifying the var in the map and associated
|
||||
+ * tracing_map_elts, or -EINVAL on error.
|
||||
+ */
|
||||
+int tracing_map_add_var(struct tracing_map *map)
|
||||
+{
|
||||
+ int ret = -EINVAL;
|
||||
+
|
||||
+ if (map->n_vars < TRACING_MAP_VARS_MAX)
|
||||
+ ret = map->n_vars++;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* tracing_map_add_key_field - Add a field describing a tracing_map key
|
||||
* @map: The tracing_map
|
||||
@@ -280,6 +369,11 @@ static void tracing_map_elt_clear(struct tracing_map_elt *elt)
|
||||
if (elt->fields[i].cmp_fn == tracing_map_cmp_atomic64)
|
||||
atomic64_set(&elt->fields[i].sum, 0);
|
||||
|
||||
+ for (i = 0; i < elt->map->n_vars; i++) {
|
||||
+ atomic64_set(&elt->vars[i], 0);
|
||||
+ elt->var_set[i] = false;
|
||||
+ }
|
||||
+
|
||||
if (elt->map->ops && elt->map->ops->elt_clear)
|
||||
elt->map->ops->elt_clear(elt);
|
||||
}
|
||||
@@ -306,6 +400,8 @@ static void tracing_map_elt_free(struct tracing_map_elt *elt)
|
||||
if (elt->map->ops && elt->map->ops->elt_free)
|
||||
elt->map->ops->elt_free(elt);
|
||||
kfree(elt->fields);
|
||||
+ kfree(elt->vars);
|
||||
+ kfree(elt->var_set);
|
||||
kfree(elt->key);
|
||||
kfree(elt);
|
||||
}
|
||||
@@ -333,6 +429,18 @@ static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map)
|
||||
goto free;
|
||||
}
|
||||
|
||||
+ elt->vars = kcalloc(map->n_vars, sizeof(*elt->vars), GFP_KERNEL);
|
||||
+ if (!elt->vars) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ elt->var_set = kcalloc(map->n_vars, sizeof(*elt->var_set), GFP_KERNEL);
|
||||
+ if (!elt->var_set) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
tracing_map_elt_init_fields(elt);
|
||||
|
||||
if (map->ops && map->ops->elt_alloc) {
|
||||
diff --git a/kernel/trace/tracing_map.h b/kernel/trace/tracing_map.h
|
||||
index de57887c0670..053eb92b2d31 100644
|
||||
--- a/kernel/trace/tracing_map.h
|
||||
+++ b/kernel/trace/tracing_map.h
|
||||
@@ -10,6 +10,7 @@
|
||||
#define TRACING_MAP_VALS_MAX 3
|
||||
#define TRACING_MAP_FIELDS_MAX (TRACING_MAP_KEYS_MAX + \
|
||||
TRACING_MAP_VALS_MAX)
|
||||
+#define TRACING_MAP_VARS_MAX 16
|
||||
#define TRACING_MAP_SORT_KEYS_MAX 2
|
||||
|
||||
typedef int (*tracing_map_cmp_fn_t) (void *val_a, void *val_b);
|
||||
@@ -137,6 +138,8 @@ struct tracing_map_field {
|
||||
struct tracing_map_elt {
|
||||
struct tracing_map *map;
|
||||
struct tracing_map_field *fields;
|
||||
+ atomic64_t *vars;
|
||||
+ bool *var_set;
|
||||
void *key;
|
||||
void *private_data;
|
||||
};
|
||||
@@ -192,6 +195,7 @@ struct tracing_map {
|
||||
int key_idx[TRACING_MAP_KEYS_MAX];
|
||||
unsigned int n_keys;
|
||||
struct tracing_map_sort_key sort_key;
|
||||
+ unsigned int n_vars;
|
||||
atomic64_t hits;
|
||||
atomic64_t drops;
|
||||
};
|
||||
@@ -241,6 +245,7 @@ tracing_map_create(unsigned int map_bits,
|
||||
extern int tracing_map_init(struct tracing_map *map);
|
||||
|
||||
extern int tracing_map_add_sum_field(struct tracing_map *map);
|
||||
+extern int tracing_map_add_var(struct tracing_map *map);
|
||||
extern int tracing_map_add_key_field(struct tracing_map *map,
|
||||
unsigned int offset,
|
||||
tracing_map_cmp_fn_t cmp_fn);
|
||||
@@ -260,7 +265,13 @@ extern int tracing_map_cmp_none(void *val_a, void *val_b);
|
||||
|
||||
extern void tracing_map_update_sum(struct tracing_map_elt *elt,
|
||||
unsigned int i, u64 n);
|
||||
+extern void tracing_map_set_var(struct tracing_map_elt *elt,
|
||||
+ unsigned int i, u64 n);
|
||||
+extern bool tracing_map_var_set(struct tracing_map_elt *elt, unsigned int i);
|
||||
extern u64 tracing_map_read_sum(struct tracing_map_elt *elt, unsigned int i);
|
||||
+extern u64 tracing_map_read_var(struct tracing_map_elt *elt, unsigned int i);
|
||||
+extern u64 tracing_map_read_var_once(struct tracing_map_elt *elt, unsigned int i);
|
||||
+
|
||||
extern void tracing_map_set_field_descr(struct tracing_map *map,
|
||||
unsigned int i,
|
||||
unsigned int key_offset,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
From fa4697e01eb4236a2e0ec5d294ae41bc0d3d8a35 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:47 -0600
|
||||
Subject: [PATCH 090/450] tracing: Add hist_data member to hist_field
|
||||
|
||||
Allow hist_data access via hist_field. Some users of hist_fields
|
||||
require or will require more access to the associated hist_data.
|
||||
|
||||
Link: http://lkml.kernel.org/r/d04cd0768f5228ebb4ac0ba4a847bc4d14d4826f.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 14ab3edac407939009700c04215935576250e969)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 14 +++++++++-----
|
||||
1 file changed, 9 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index cd80bca34cec..ec58902145e9 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -39,6 +39,7 @@ struct hist_field {
|
||||
unsigned int offset;
|
||||
unsigned int is_signed;
|
||||
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
+ struct hist_trigger_data *hist_data;
|
||||
};
|
||||
|
||||
static u64 hist_field_none(struct hist_field *field, void *event,
|
||||
@@ -420,7 +421,8 @@ static void destroy_hist_field(struct hist_field *hist_field,
|
||||
kfree(hist_field);
|
||||
}
|
||||
|
||||
-static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
+static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
+ struct ftrace_event_field *field,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct hist_field *hist_field;
|
||||
@@ -432,6 +434,8 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
if (!hist_field)
|
||||
return NULL;
|
||||
|
||||
+ hist_field->hist_data = hist_data;
|
||||
+
|
||||
if (flags & HIST_FIELD_FL_HITCOUNT) {
|
||||
hist_field->fn = hist_field_counter;
|
||||
goto out;
|
||||
@@ -445,7 +449,7 @@ static struct hist_field *create_hist_field(struct ftrace_event_field *field,
|
||||
if (flags & HIST_FIELD_FL_LOG2) {
|
||||
unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
|
||||
hist_field->fn = hist_field_log2;
|
||||
- hist_field->operands[0] = create_hist_field(field, fl);
|
||||
+ hist_field->operands[0] = create_hist_field(hist_data, field, fl);
|
||||
hist_field->size = hist_field->operands[0]->size;
|
||||
goto out;
|
||||
}
|
||||
@@ -498,7 +502,7 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
static int create_hitcount_val(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
hist_data->fields[HITCOUNT_IDX] =
|
||||
- create_hist_field(NULL, HIST_FIELD_FL_HITCOUNT);
|
||||
+ create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT);
|
||||
if (!hist_data->fields[HITCOUNT_IDX])
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -544,7 +548,7 @@ static int create_val_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- hist_data->fields[val_idx] = create_hist_field(field, flags);
|
||||
+ hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags);
|
||||
if (!hist_data->fields[val_idx]) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@@ -654,7 +658,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- hist_data->fields[key_idx] = create_hist_field(field, flags);
|
||||
+ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags);
|
||||
if (!hist_data->fields[key_idx]) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
From 89f6ad2173cbe3a78397f9a3ed07de978840b30d Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:48 -0600
|
||||
Subject: [PATCH 091/450] tracing: Add usecs modifier for hist trigger
|
||||
timestamps
|
||||
|
||||
Appending .usecs onto a common_timestamp field will cause the
|
||||
timestamp value to be in microseconds instead of the default
|
||||
nanoseconds. A typical latency histogram using usecs would look like
|
||||
this:
|
||||
|
||||
# echo 'hist:keys=pid,prio:ts0=common_timestamp.usecs ...
|
||||
# echo 'hist:keys=next_pid:wakeup_lat=common_timestamp.usecs-$ts0 ...
|
||||
|
||||
This also adds an external trace_clock_in_ns() to trace.c for the
|
||||
timestamp conversion.
|
||||
|
||||
Link: http://lkml.kernel.org/r/4e813705a170b3e13e97dc3135047362fb1a39f3.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 4fa4fdb0fe5d0e87e05b0c5b443cec2269ec0609)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
Documentation/trace/histogram.txt | 1 +
|
||||
kernel/trace/trace.c | 13 +++++++++++--
|
||||
kernel/trace/trace.h | 2 ++
|
||||
kernel/trace/trace_events_hist.c | 28 ++++++++++++++++++++++------
|
||||
4 files changed, 36 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/Documentation/trace/histogram.txt b/Documentation/trace/histogram.txt
|
||||
index a4143f04a097..25c94730d3fe 100644
|
||||
--- a/Documentation/trace/histogram.txt
|
||||
+++ b/Documentation/trace/histogram.txt
|
||||
@@ -74,6 +74,7 @@
|
||||
.syscall display a syscall id as a system call name
|
||||
.execname display a common_pid as a program name
|
||||
.log2 display log2 value rather than raw number
|
||||
+ .usecs display a common_timestamp in microseconds
|
||||
|
||||
Note that in general the semantics of a given field aren't
|
||||
interpreted when applying a modifier to it, but there are some
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index c358cae7461f..1f695da375df 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -1170,6 +1170,14 @@ static struct {
|
||||
ARCH_TRACE_CLOCKS
|
||||
};
|
||||
|
||||
+bool trace_clock_in_ns(struct trace_array *tr)
|
||||
+{
|
||||
+ if (trace_clocks[tr->clock_id].in_ns)
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* trace_parser_get_init - gets the buffer for trace parser
|
||||
*/
|
||||
@@ -4710,8 +4718,9 @@ static const char readme_msg[] =
|
||||
"\t .sym display an address as a symbol\n"
|
||||
"\t .sym-offset display an address as a symbol and offset\n"
|
||||
"\t .execname display a common_pid as a program name\n"
|
||||
- "\t .syscall display a syscall id as a syscall name\n\n"
|
||||
- "\t .log2 display log2 value rather than raw number\n\n"
|
||||
+ "\t .syscall display a syscall id as a syscall name\n"
|
||||
+ "\t .log2 display log2 value rather than raw number\n"
|
||||
+ "\t .usecs display a common_timestamp in microseconds\n\n"
|
||||
"\t The 'pause' parameter can be used to pause an existing hist\n"
|
||||
"\t trigger or to start a hist trigger but not log any events\n"
|
||||
"\t until told to do so. 'continue' can be used to start or\n"
|
||||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
|
||||
index e2e12710d53c..92936a6fc29e 100644
|
||||
--- a/kernel/trace/trace.h
|
||||
+++ b/kernel/trace/trace.h
|
||||
@@ -289,6 +289,8 @@ extern void trace_array_put(struct trace_array *tr);
|
||||
|
||||
extern int tracing_set_time_stamp_abs(struct trace_array *tr, bool abs);
|
||||
|
||||
+extern bool trace_clock_in_ns(struct trace_array *tr);
|
||||
+
|
||||
/*
|
||||
* The global tracer (top) should be the first trace array added,
|
||||
* but we check the flag anyway.
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index ec58902145e9..6d268e25d051 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -90,12 +90,6 @@ static u64 hist_field_log2(struct hist_field *hist_field, void *event,
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
|
||||
-static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
-{
|
||||
- return ring_buffer_event_time_stamp(rbe);
|
||||
-}
|
||||
-
|
||||
#define DEFINE_HIST_FIELD_FN(type) \
|
||||
static u64 hist_field_##type(struct hist_field *hist_field, \
|
||||
void *event, \
|
||||
@@ -143,6 +137,7 @@ enum hist_field_flags {
|
||||
HIST_FIELD_FL_STACKTRACE = 1 << 8,
|
||||
HIST_FIELD_FL_LOG2 = 1 << 9,
|
||||
HIST_FIELD_FL_TIMESTAMP = 1 << 10,
|
||||
+ HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11,
|
||||
};
|
||||
|
||||
struct hist_trigger_attrs {
|
||||
@@ -153,6 +148,7 @@ struct hist_trigger_attrs {
|
||||
bool pause;
|
||||
bool cont;
|
||||
bool clear;
|
||||
+ bool ts_in_usecs;
|
||||
unsigned int map_bits;
|
||||
};
|
||||
|
||||
@@ -170,6 +166,20 @@ struct hist_trigger_data {
|
||||
bool enable_timestamps;
|
||||
};
|
||||
|
||||
+static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
+{
|
||||
+ struct hist_trigger_data *hist_data = hist_field->hist_data;
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+
|
||||
+ u64 ts = ring_buffer_event_time_stamp(rbe);
|
||||
+
|
||||
+ if (hist_data->attrs->ts_in_usecs && trace_clock_in_ns(tr))
|
||||
+ ts = ns2usecs(ts);
|
||||
+
|
||||
+ return ts;
|
||||
+}
|
||||
+
|
||||
static const char *hist_field_name(struct hist_field *field,
|
||||
unsigned int level)
|
||||
{
|
||||
@@ -634,6 +644,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
flags |= HIST_FIELD_FL_SYSCALL;
|
||||
else if (strcmp(field_str, "log2") == 0)
|
||||
flags |= HIST_FIELD_FL_LOG2;
|
||||
+ else if (strcmp(field_str, "usecs") == 0)
|
||||
+ flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
|
||||
else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -643,6 +655,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
hist_data->enable_timestamps = true;
|
||||
+ if (flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
+ hist_data->attrs->ts_in_usecs = true;
|
||||
key_size = sizeof(u64);
|
||||
} else {
|
||||
field = trace_find_event_field(file->event_call, field_name);
|
||||
@@ -1243,6 +1257,8 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
flags_str = "syscall";
|
||||
else if (hist_field->flags & HIST_FIELD_FL_LOG2)
|
||||
flags_str = "log2";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
+ flags_str = "usecs";
|
||||
|
||||
return flags_str;
|
||||
}
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,788 +0,0 @@
|
||||
From 854d042f0bf85296ca85144c48f66475651cdaed Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:49 -0600
|
||||
Subject: [PATCH 092/450] tracing: Add variable support to hist triggers
|
||||
|
||||
Add support for saving the value of a current event's event field by
|
||||
assigning it to a variable that can be read by a subsequent event.
|
||||
|
||||
The basic syntax for saving a variable is to simply prefix a unique
|
||||
variable name not corresponding to any keyword along with an '=' sign
|
||||
to any event field.
|
||||
|
||||
Both keys and values can be saved and retrieved in this way:
|
||||
|
||||
# echo 'hist:keys=next_pid:vals=$ts0:ts0=common_timestamp ...
|
||||
# echo 'hist:timer_pid=common_pid:key=$timer_pid ...'
|
||||
|
||||
If a variable isn't a key variable or prefixed with 'vals=', the
|
||||
associated event field will be saved in a variable but won't be summed
|
||||
as a value:
|
||||
|
||||
# echo 'hist:keys=next_pid:ts1=common_timestamp:...
|
||||
|
||||
Multiple variables can be assigned at the same time:
|
||||
|
||||
# echo 'hist:keys=pid:vals=$ts0,$b,field2:ts0=common_timestamp,b=field1 ...
|
||||
|
||||
Multiple (or single) variables can also be assigned at the same time
|
||||
using separate assignments:
|
||||
|
||||
# echo 'hist:keys=pid:vals=$ts0:ts0=common_timestamp:b=field1:c=field2 ...
|
||||
|
||||
Variables set as above can be used by being referenced from another
|
||||
event, as described in a subsequent patch.
|
||||
|
||||
Link: http://lkml.kernel.org/r/fc93c4944d9719dbcb1d0067be627d44e98e2adc.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Baohong Liu <baohong.liu@intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit b073711690e3af61965e53f197a56638b3c65a81)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 370 +++++++++++++++++++++++++++----
|
||||
1 file changed, 331 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 6d268e25d051..b4301542bb4a 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -30,6 +30,13 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
|
||||
struct ring_buffer_event *rbe);
|
||||
|
||||
#define HIST_FIELD_OPERANDS_MAX 2
|
||||
+#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
|
||||
+
|
||||
+struct hist_var {
|
||||
+ char *name;
|
||||
+ struct hist_trigger_data *hist_data;
|
||||
+ unsigned int idx;
|
||||
+};
|
||||
|
||||
struct hist_field {
|
||||
struct ftrace_event_field *field;
|
||||
@@ -40,6 +47,7 @@ struct hist_field {
|
||||
unsigned int is_signed;
|
||||
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
struct hist_trigger_data *hist_data;
|
||||
+ struct hist_var var;
|
||||
};
|
||||
|
||||
static u64 hist_field_none(struct hist_field *field, void *event,
|
||||
@@ -138,6 +146,13 @@ enum hist_field_flags {
|
||||
HIST_FIELD_FL_LOG2 = 1 << 9,
|
||||
HIST_FIELD_FL_TIMESTAMP = 1 << 10,
|
||||
HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11,
|
||||
+ HIST_FIELD_FL_VAR = 1 << 12,
|
||||
+};
|
||||
+
|
||||
+struct var_defs {
|
||||
+ unsigned int n_vars;
|
||||
+ char *name[TRACING_MAP_VARS_MAX];
|
||||
+ char *expr[TRACING_MAP_VARS_MAX];
|
||||
};
|
||||
|
||||
struct hist_trigger_attrs {
|
||||
@@ -150,13 +165,19 @@ struct hist_trigger_attrs {
|
||||
bool clear;
|
||||
bool ts_in_usecs;
|
||||
unsigned int map_bits;
|
||||
+
|
||||
+ char *assignment_str[TRACING_MAP_VARS_MAX];
|
||||
+ unsigned int n_assignments;
|
||||
+
|
||||
+ struct var_defs var_defs;
|
||||
};
|
||||
|
||||
struct hist_trigger_data {
|
||||
- struct hist_field *fields[TRACING_MAP_FIELDS_MAX];
|
||||
+ struct hist_field *fields[HIST_FIELDS_MAX];
|
||||
unsigned int n_vals;
|
||||
unsigned int n_keys;
|
||||
unsigned int n_fields;
|
||||
+ unsigned int n_vars;
|
||||
unsigned int key_size;
|
||||
struct tracing_map_sort_key sort_keys[TRACING_MAP_SORT_KEYS_MAX];
|
||||
unsigned int n_sort_keys;
|
||||
@@ -164,6 +185,7 @@ struct hist_trigger_data {
|
||||
struct hist_trigger_attrs *attrs;
|
||||
struct tracing_map *map;
|
||||
bool enable_timestamps;
|
||||
+ bool remove;
|
||||
};
|
||||
|
||||
static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
@@ -180,6 +202,48 @@ static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
return ts;
|
||||
}
|
||||
|
||||
+static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
|
||||
+ const char *var_name)
|
||||
+{
|
||||
+ struct hist_field *hist_field, *found = NULL;
|
||||
+ int i;
|
||||
+
|
||||
+ for_each_hist_field(i, hist_data) {
|
||||
+ hist_field = hist_data->fields[i];
|
||||
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR &&
|
||||
+ strcmp(hist_field->var.name, var_name) == 0) {
|
||||
+ found = hist_field;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *find_var(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file,
|
||||
+ const char *var_name)
|
||||
+{
|
||||
+ struct hist_trigger_data *test_data;
|
||||
+ struct event_trigger_data *test;
|
||||
+ struct hist_field *hist_field;
|
||||
+
|
||||
+ hist_field = find_var_field(hist_data, var_name);
|
||||
+ if (hist_field)
|
||||
+ return hist_field;
|
||||
+
|
||||
+ list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
+ test_data = test->private_data;
|
||||
+ hist_field = find_var_field(test_data, var_name);
|
||||
+ if (hist_field)
|
||||
+ return hist_field;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
static const char *hist_field_name(struct hist_field *field,
|
||||
unsigned int level)
|
||||
{
|
||||
@@ -262,9 +326,14 @@ static int parse_map_size(char *str)
|
||||
|
||||
static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
|
||||
{
|
||||
+ unsigned int i;
|
||||
+
|
||||
if (!attrs)
|
||||
return;
|
||||
|
||||
+ for (i = 0; i < attrs->n_assignments; i++)
|
||||
+ kfree(attrs->assignment_str[i]);
|
||||
+
|
||||
kfree(attrs->name);
|
||||
kfree(attrs->sort_key_str);
|
||||
kfree(attrs->keys_str);
|
||||
@@ -311,8 +380,22 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
|
||||
goto out;
|
||||
}
|
||||
attrs->map_bits = map_bits;
|
||||
- } else
|
||||
- ret = -EINVAL;
|
||||
+ } else {
|
||||
+ char *assignment;
|
||||
+
|
||||
+ if (attrs->n_assignments == TRACING_MAP_VARS_MAX) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ assignment = kstrdup(str, GFP_KERNEL);
|
||||
+ if (!assignment) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ attrs->assignment_str[attrs->n_assignments++] = assignment;
|
||||
+ }
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -428,12 +511,15 @@ static void destroy_hist_field(struct hist_field *hist_field,
|
||||
for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++)
|
||||
destroy_hist_field(hist_field->operands[i], level + 1);
|
||||
|
||||
+ kfree(hist_field->var.name);
|
||||
+
|
||||
kfree(hist_field);
|
||||
}
|
||||
|
||||
static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
struct ftrace_event_field *field,
|
||||
- unsigned long flags)
|
||||
+ unsigned long flags,
|
||||
+ char *var_name)
|
||||
{
|
||||
struct hist_field *hist_field;
|
||||
|
||||
@@ -459,7 +545,7 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
if (flags & HIST_FIELD_FL_LOG2) {
|
||||
unsigned long fl = flags & ~HIST_FIELD_FL_LOG2;
|
||||
hist_field->fn = hist_field_log2;
|
||||
- hist_field->operands[0] = create_hist_field(hist_data, field, fl);
|
||||
+ hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
|
||||
hist_field->size = hist_field->operands[0]->size;
|
||||
goto out;
|
||||
}
|
||||
@@ -494,14 +580,23 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
hist_field->field = field;
|
||||
hist_field->flags = flags;
|
||||
|
||||
+ if (var_name) {
|
||||
+ hist_field->var.name = kstrdup(var_name, GFP_KERNEL);
|
||||
+ if (!hist_field->var.name)
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
return hist_field;
|
||||
+ free:
|
||||
+ destroy_hist_field(hist_field, 0);
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
- for (i = 0; i < TRACING_MAP_FIELDS_MAX; i++) {
|
||||
+ for (i = 0; i < HIST_FIELDS_MAX; i++) {
|
||||
if (hist_data->fields[i]) {
|
||||
destroy_hist_field(hist_data->fields[i], 0);
|
||||
hist_data->fields[i] = NULL;
|
||||
@@ -512,11 +607,12 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
static int create_hitcount_val(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
hist_data->fields[HITCOUNT_IDX] =
|
||||
- create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT);
|
||||
+ create_hist_field(hist_data, NULL, HIST_FIELD_FL_HITCOUNT, NULL);
|
||||
if (!hist_data->fields[HITCOUNT_IDX])
|
||||
return -ENOMEM;
|
||||
|
||||
hist_data->n_vals++;
|
||||
+ hist_data->n_fields++;
|
||||
|
||||
if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
|
||||
return -EINVAL;
|
||||
@@ -524,19 +620,16 @@ static int create_hitcount_val(struct hist_trigger_data *hist_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int create_val_field(struct hist_trigger_data *hist_data,
|
||||
- unsigned int val_idx,
|
||||
- struct trace_event_file *file,
|
||||
- char *field_str)
|
||||
+static int __create_val_field(struct hist_trigger_data *hist_data,
|
||||
+ unsigned int val_idx,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *var_name, char *field_str,
|
||||
+ unsigned long flags)
|
||||
{
|
||||
struct ftrace_event_field *field = NULL;
|
||||
- unsigned long flags = 0;
|
||||
char *field_name;
|
||||
int ret = 0;
|
||||
|
||||
- if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX))
|
||||
- return -EINVAL;
|
||||
-
|
||||
field_name = strsep(&field_str, ".");
|
||||
if (field_str) {
|
||||
if (strcmp(field_str, "hex") == 0)
|
||||
@@ -558,25 +651,58 @@ static int create_val_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags);
|
||||
+ hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags, var_name);
|
||||
if (!hist_data->fields[val_idx]) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
++hist_data->n_vals;
|
||||
+ ++hist_data->n_fields;
|
||||
|
||||
- if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX))
|
||||
+ if (WARN_ON(hist_data->n_vals > TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int create_val_field(struct hist_trigger_data *hist_data,
|
||||
+ unsigned int val_idx,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *field_str)
|
||||
+{
|
||||
+ if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return __create_val_field(hist_data, val_idx, file, NULL, field_str, 0);
|
||||
+}
|
||||
+
|
||||
+static int create_var_field(struct hist_trigger_data *hist_data,
|
||||
+ unsigned int val_idx,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *var_name, char *expr_str)
|
||||
+{
|
||||
+ unsigned long flags = 0;
|
||||
+
|
||||
+ if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
|
||||
+ return -EINVAL;
|
||||
+ if (find_var(hist_data, file, var_name) && !hist_data->remove) {
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ flags |= HIST_FIELD_FL_VAR;
|
||||
+ hist_data->n_vars++;
|
||||
+ if (WARN_ON(hist_data->n_vars > TRACING_MAP_VARS_MAX))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return __create_val_field(hist_data, val_idx, file, var_name, expr_str, flags);
|
||||
+}
|
||||
+
|
||||
static int create_val_fields(struct hist_trigger_data *hist_data,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
char *fields_str, *field_str;
|
||||
- unsigned int i, j;
|
||||
+ unsigned int i, j = 1;
|
||||
int ret;
|
||||
|
||||
ret = create_hitcount_val(hist_data);
|
||||
@@ -596,12 +722,15 @@ static int create_val_fields(struct hist_trigger_data *hist_data,
|
||||
field_str = strsep(&fields_str, ",");
|
||||
if (!field_str)
|
||||
break;
|
||||
+
|
||||
if (strcmp(field_str, "hitcount") == 0)
|
||||
continue;
|
||||
+
|
||||
ret = create_val_field(hist_data, j++, file, field_str);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
+
|
||||
if (fields_str && (strcmp(fields_str, "hitcount") != 0))
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
@@ -615,11 +744,12 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
char *field_str)
|
||||
{
|
||||
struct ftrace_event_field *field = NULL;
|
||||
+ struct hist_field *hist_field = NULL;
|
||||
unsigned long flags = 0;
|
||||
unsigned int key_size;
|
||||
int ret = 0;
|
||||
|
||||
- if (WARN_ON(key_idx >= TRACING_MAP_FIELDS_MAX))
|
||||
+ if (WARN_ON(key_idx >= HIST_FIELDS_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
flags |= HIST_FIELD_FL_KEY;
|
||||
@@ -627,6 +757,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
if (strcmp(field_str, "stacktrace") == 0) {
|
||||
flags |= HIST_FIELD_FL_STACKTRACE;
|
||||
key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH;
|
||||
+ hist_field = create_hist_field(hist_data, NULL, flags, NULL);
|
||||
} else {
|
||||
char *field_name = strsep(&field_str, ".");
|
||||
|
||||
@@ -672,7 +803,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
}
|
||||
|
||||
- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags);
|
||||
+ hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, NULL);
|
||||
if (!hist_data->fields[key_idx]) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
@@ -688,6 +819,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
}
|
||||
|
||||
hist_data->n_keys++;
|
||||
+ hist_data->n_fields++;
|
||||
|
||||
if (WARN_ON(hist_data->n_keys > TRACING_MAP_KEYS_MAX))
|
||||
return -EINVAL;
|
||||
@@ -731,21 +863,111 @@ static int create_key_fields(struct hist_trigger_data *hist_data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int create_var_fields(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file)
|
||||
+{
|
||||
+ unsigned int i, j = hist_data->n_vals;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ unsigned int n_vars = hist_data->attrs->var_defs.n_vars;
|
||||
+
|
||||
+ for (i = 0; i < n_vars; i++) {
|
||||
+ char *var_name = hist_data->attrs->var_defs.name[i];
|
||||
+ char *expr = hist_data->attrs->var_defs.expr[i];
|
||||
+
|
||||
+ ret = create_var_field(hist_data, j++, file, var_name, expr);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+ }
|
||||
+ out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void free_var_defs(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) {
|
||||
+ kfree(hist_data->attrs->var_defs.name[i]);
|
||||
+ kfree(hist_data->attrs->var_defs.expr[i]);
|
||||
+ }
|
||||
+
|
||||
+ hist_data->attrs->var_defs.n_vars = 0;
|
||||
+}
|
||||
+
|
||||
+static int parse_var_defs(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ char *s, *str, *var_name, *field_str;
|
||||
+ unsigned int i, j, n_vars = 0;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->attrs->n_assignments; i++) {
|
||||
+ str = hist_data->attrs->assignment_str[i];
|
||||
+ for (j = 0; j < TRACING_MAP_VARS_MAX; j++) {
|
||||
+ field_str = strsep(&str, ",");
|
||||
+ if (!field_str)
|
||||
+ break;
|
||||
+
|
||||
+ var_name = strsep(&field_str, "=");
|
||||
+ if (!var_name || !field_str) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ if (n_vars == TRACING_MAP_VARS_MAX) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ s = kstrdup(var_name, GFP_KERNEL);
|
||||
+ if (!s) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+ hist_data->attrs->var_defs.name[n_vars] = s;
|
||||
+
|
||||
+ s = kstrdup(field_str, GFP_KERNEL);
|
||||
+ if (!s) {
|
||||
+ kfree(hist_data->attrs->var_defs.name[n_vars]);
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+ hist_data->attrs->var_defs.expr[n_vars++] = s;
|
||||
+
|
||||
+ hist_data->attrs->var_defs.n_vars = n_vars;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+ free:
|
||||
+ free_var_defs(hist_data);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int create_hist_fields(struct hist_trigger_data *hist_data,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
int ret;
|
||||
|
||||
+ ret = parse_var_defs(hist_data);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
ret = create_val_fields(hist_data, file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
- ret = create_key_fields(hist_data, file);
|
||||
+ ret = create_var_fields(hist_data, file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
- hist_data->n_fields = hist_data->n_vals + hist_data->n_keys;
|
||||
+ ret = create_key_fields(hist_data, file);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
out:
|
||||
+ free_var_defs(hist_data);
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -768,7 +990,7 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
char *fields_str = hist_data->attrs->sort_key_str;
|
||||
struct tracing_map_sort_key *sort_key;
|
||||
int descending, ret = 0;
|
||||
- unsigned int i, j;
|
||||
+ unsigned int i, j, k;
|
||||
|
||||
hist_data->n_sort_keys = 1; /* we always have at least one, hitcount */
|
||||
|
||||
@@ -816,12 +1038,19 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
continue;
|
||||
}
|
||||
|
||||
- for (j = 1; j < hist_data->n_fields; j++) {
|
||||
+ for (j = 1, k = 1; j < hist_data->n_fields; j++) {
|
||||
+ unsigned int idx;
|
||||
+
|
||||
hist_field = hist_data->fields[j];
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR)
|
||||
+ continue;
|
||||
+
|
||||
+ idx = k++;
|
||||
+
|
||||
test_name = hist_field_name(hist_field, 0);
|
||||
|
||||
if (strcmp(field_name, test_name) == 0) {
|
||||
- sort_key->field_idx = j;
|
||||
+ sort_key->field_idx = idx;
|
||||
descending = is_descending(field_str);
|
||||
if (descending < 0) {
|
||||
ret = descending;
|
||||
@@ -836,6 +1065,7 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
break;
|
||||
}
|
||||
}
|
||||
+
|
||||
hist_data->n_sort_keys = i;
|
||||
out:
|
||||
return ret;
|
||||
@@ -876,12 +1106,19 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
|
||||
idx = tracing_map_add_key_field(map,
|
||||
hist_field->offset,
|
||||
cmp_fn);
|
||||
-
|
||||
- } else
|
||||
+ } else if (!(hist_field->flags & HIST_FIELD_FL_VAR))
|
||||
idx = tracing_map_add_sum_field(map);
|
||||
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
+
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ idx = tracing_map_add_var(map);
|
||||
+ if (idx < 0)
|
||||
+ return idx;
|
||||
+ hist_field->var.idx = idx;
|
||||
+ hist_field->var.hist_data = hist_data;
|
||||
+ }
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -905,7 +1142,8 @@ static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
|
||||
static struct hist_trigger_data *
|
||||
create_hist_data(unsigned int map_bits,
|
||||
struct hist_trigger_attrs *attrs,
|
||||
- struct trace_event_file *file)
|
||||
+ struct trace_event_file *file,
|
||||
+ bool remove)
|
||||
{
|
||||
const struct tracing_map_ops *map_ops = NULL;
|
||||
struct hist_trigger_data *hist_data;
|
||||
@@ -916,6 +1154,7 @@ create_hist_data(unsigned int map_bits,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hist_data->attrs = attrs;
|
||||
+ hist_data->remove = remove;
|
||||
|
||||
ret = create_hist_fields(hist_data, file);
|
||||
if (ret)
|
||||
@@ -962,14 +1201,28 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
struct ring_buffer_event *rbe)
|
||||
{
|
||||
struct hist_field *hist_field;
|
||||
- unsigned int i;
|
||||
+ unsigned int i, var_idx;
|
||||
u64 hist_val;
|
||||
|
||||
for_each_hist_val_field(i, hist_data) {
|
||||
hist_field = hist_data->fields[i];
|
||||
hist_val = hist_field->fn(hist_field, rec, rbe);
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ var_idx = hist_field->var.idx;
|
||||
+ tracing_map_set_var(elt, var_idx, hist_val);
|
||||
+ continue;
|
||||
+ }
|
||||
tracing_map_update_sum(elt, i, hist_val);
|
||||
}
|
||||
+
|
||||
+ for_each_hist_key_field(i, hist_data) {
|
||||
+ hist_field = hist_data->fields[i];
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ hist_val = hist_field->fn(hist_field, rec, rbe);
|
||||
+ var_idx = hist_field->var.idx;
|
||||
+ tracing_map_set_var(elt, var_idx, hist_val);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
static inline void add_to_key(char *compound_key, void *key,
|
||||
@@ -1144,6 +1397,9 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
for (i = 1; i < hist_data->n_vals; i++) {
|
||||
field_name = hist_field_name(hist_data->fields[i], 0);
|
||||
|
||||
+ if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR)
|
||||
+ continue;
|
||||
+
|
||||
if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) {
|
||||
seq_printf(m, " %s: %10llx", field_name,
|
||||
tracing_map_read_sum(elt, i));
|
||||
@@ -1267,6 +1523,9 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
||||
{
|
||||
const char *field_name = hist_field_name(hist_field, 0);
|
||||
|
||||
+ if (hist_field->var.name)
|
||||
+ seq_printf(m, "%s=", hist_field->var.name);
|
||||
+
|
||||
if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
seq_puts(m, "common_timestamp");
|
||||
else if (field_name)
|
||||
@@ -1285,7 +1544,8 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
struct event_trigger_data *data)
|
||||
{
|
||||
struct hist_trigger_data *hist_data = data->private_data;
|
||||
- struct hist_field *key_field;
|
||||
+ struct hist_field *field;
|
||||
+ bool have_var = false;
|
||||
unsigned int i;
|
||||
|
||||
seq_puts(m, "hist:");
|
||||
@@ -1296,25 +1556,47 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
seq_puts(m, "keys=");
|
||||
|
||||
for_each_hist_key_field(i, hist_data) {
|
||||
- key_field = hist_data->fields[i];
|
||||
+ field = hist_data->fields[i];
|
||||
|
||||
if (i > hist_data->n_vals)
|
||||
seq_puts(m, ",");
|
||||
|
||||
- if (key_field->flags & HIST_FIELD_FL_STACKTRACE)
|
||||
+ if (field->flags & HIST_FIELD_FL_STACKTRACE)
|
||||
seq_puts(m, "stacktrace");
|
||||
else
|
||||
- hist_field_print(m, key_field);
|
||||
+ hist_field_print(m, field);
|
||||
}
|
||||
|
||||
seq_puts(m, ":vals=");
|
||||
|
||||
for_each_hist_val_field(i, hist_data) {
|
||||
+ field = hist_data->fields[i];
|
||||
+ if (field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ have_var = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
if (i == HITCOUNT_IDX)
|
||||
seq_puts(m, "hitcount");
|
||||
else {
|
||||
seq_puts(m, ",");
|
||||
- hist_field_print(m, hist_data->fields[i]);
|
||||
+ hist_field_print(m, field);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (have_var) {
|
||||
+ unsigned int n = 0;
|
||||
+
|
||||
+ seq_puts(m, ":");
|
||||
+
|
||||
+ for_each_hist_val_field(i, hist_data) {
|
||||
+ field = hist_data->fields[i];
|
||||
+
|
||||
+ if (field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ if (n++)
|
||||
+ seq_puts(m, ",");
|
||||
+ hist_field_print(m, field);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1322,7 +1604,10 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
|
||||
for (i = 0; i < hist_data->n_sort_keys; i++) {
|
||||
struct tracing_map_sort_key *sort_key;
|
||||
- unsigned int idx;
|
||||
+ unsigned int idx, first_key_idx;
|
||||
+
|
||||
+ /* skip VAR vals */
|
||||
+ first_key_idx = hist_data->n_vals - hist_data->n_vars;
|
||||
|
||||
sort_key = &hist_data->sort_keys[i];
|
||||
idx = sort_key->field_idx;
|
||||
@@ -1335,8 +1620,11 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
|
||||
if (idx == HITCOUNT_IDX)
|
||||
seq_puts(m, "hitcount");
|
||||
- else
|
||||
+ else {
|
||||
+ if (idx >= first_key_idx)
|
||||
+ idx += hist_data->n_vars;
|
||||
hist_field_print(m, hist_data->fields[idx]);
|
||||
+ }
|
||||
|
||||
if (sort_key->descending)
|
||||
seq_puts(m, ".descending");
|
||||
@@ -1633,7 +1921,7 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
test->ops->free(test->ops, test);
|
||||
|
||||
if (hist_data->enable_timestamps) {
|
||||
- if (unregistered)
|
||||
+ if (!hist_data->remove || unregistered)
|
||||
tracing_set_time_stamp_abs(file->tr, false);
|
||||
}
|
||||
}
|
||||
@@ -1666,12 +1954,16 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
struct hist_trigger_attrs *attrs;
|
||||
struct event_trigger_ops *trigger_ops;
|
||||
struct hist_trigger_data *hist_data;
|
||||
+ bool remove = false;
|
||||
char *trigger;
|
||||
int ret = 0;
|
||||
|
||||
if (!param)
|
||||
return -EINVAL;
|
||||
|
||||
+ if (glob[0] == '!')
|
||||
+ remove = true;
|
||||
+
|
||||
/* separate the trigger from the filter (k:v [if filter]) */
|
||||
trigger = strsep(¶m, " \t");
|
||||
if (!trigger)
|
||||
@@ -1684,7 +1976,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
if (attrs->map_bits)
|
||||
hist_trigger_bits = attrs->map_bits;
|
||||
|
||||
- hist_data = create_hist_data(hist_trigger_bits, attrs, file);
|
||||
+ hist_data = create_hist_data(hist_trigger_bits, attrs, file, remove);
|
||||
if (IS_ERR(hist_data)) {
|
||||
destroy_hist_trigger_attrs(attrs);
|
||||
return PTR_ERR(hist_data);
|
||||
@@ -1713,7 +2005,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
- if (glob[0] == '!') {
|
||||
+ if (remove) {
|
||||
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
|
||||
ret = 0;
|
||||
goto out_free;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
From 3d8b3cac2bd3b3898bb0d37555cf677a1e9d9160 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:50 -0600
|
||||
Subject: [PATCH 093/450] tracing: Account for variables in named trigger
|
||||
compatibility
|
||||
|
||||
Named triggers must also have the same set of variables in order to be
|
||||
considered compatible - update the trigger match test to account for
|
||||
that.
|
||||
|
||||
The reason for this requirement is that named triggers with variables
|
||||
are meant to allow one or more events to set the same variable.
|
||||
|
||||
Link: http://lkml.kernel.org/r/a17eae6328a99917f9d5c66129c9fcd355279ee9.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit f94add7df3d72bc8e659f9491e25d91c9dae1b44)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index b4301542bb4a..68ff2491edd0 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -1612,7 +1612,7 @@ static int event_hist_trigger_print(struct seq_file *m,
|
||||
sort_key = &hist_data->sort_keys[i];
|
||||
idx = sort_key->field_idx;
|
||||
|
||||
- if (WARN_ON(idx >= TRACING_MAP_FIELDS_MAX))
|
||||
+ if (WARN_ON(idx >= HIST_FIELDS_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
if (i > 0)
|
||||
@@ -1800,6 +1800,11 @@ static bool hist_trigger_match(struct event_trigger_data *data,
|
||||
return false;
|
||||
if (key_field->is_signed != key_field_test->is_signed)
|
||||
return false;
|
||||
+ if (!!key_field->var.name != !!key_field_test->var.name)
|
||||
+ return false;
|
||||
+ if (key_field->var.name &&
|
||||
+ strcmp(key_field->var.name, key_field_test->var.name) != 0)
|
||||
+ return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < hist_data->n_sort_keys; i++) {
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
From 01d1a46a1261ddc3dd9342e975f4ec39a2da543d Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:51 -0600
|
||||
Subject: [PATCH 094/450] tracing: Move get_hist_field_flags()
|
||||
|
||||
Move get_hist_field_flags() to make it more easily accessible for new
|
||||
code (and keep the move separate from new functionality).
|
||||
|
||||
Link: http://lkml.kernel.org/r/32470f0a7047ec7a6e84ba5ec89d6142cc6ede7d.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit fde3bce553d359c01beb9a6fce4013b65076aff3)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 44 ++++++++++++++++----------------
|
||||
1 file changed, 22 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 68ff2491edd0..c6c24d6d2be8 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -497,6 +497,28 @@ static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
|
||||
.elt_init = hist_trigger_elt_comm_init,
|
||||
};
|
||||
|
||||
+static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
+{
|
||||
+ const char *flags_str = NULL;
|
||||
+
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_HEX)
|
||||
+ flags_str = "hex";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_SYM)
|
||||
+ flags_str = "sym";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET)
|
||||
+ flags_str = "sym-offset";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_EXECNAME)
|
||||
+ flags_str = "execname";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_SYSCALL)
|
||||
+ flags_str = "syscall";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_LOG2)
|
||||
+ flags_str = "log2";
|
||||
+ else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
+ flags_str = "usecs";
|
||||
+
|
||||
+ return flags_str;
|
||||
+}
|
||||
+
|
||||
static void destroy_hist_field(struct hist_field *hist_field,
|
||||
unsigned int level)
|
||||
{
|
||||
@@ -1497,28 +1519,6 @@ const struct file_operations event_hist_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
-static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
-{
|
||||
- const char *flags_str = NULL;
|
||||
-
|
||||
- if (hist_field->flags & HIST_FIELD_FL_HEX)
|
||||
- flags_str = "hex";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_SYM)
|
||||
- flags_str = "sym";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_SYM_OFFSET)
|
||||
- flags_str = "sym-offset";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_EXECNAME)
|
||||
- flags_str = "execname";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_SYSCALL)
|
||||
- flags_str = "syscall";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_LOG2)
|
||||
- flags_str = "log2";
|
||||
- else if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
- flags_str = "usecs";
|
||||
-
|
||||
- return flags_str;
|
||||
-}
|
||||
-
|
||||
static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
||||
{
|
||||
const char *field_name = hist_field_name(hist_field, 0);
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,634 +0,0 @@
|
||||
From f7d4aeb6cc78f11fa4826c0d18b2807c976f4aee Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:52 -0600
|
||||
Subject: [PATCH 095/450] tracing: Add simple expression support to hist
|
||||
triggers
|
||||
|
||||
Add support for simple addition, subtraction, and unary expressions
|
||||
(-(expr) and expr, where expr = b-a, a+b, a+b+c) to hist triggers, in
|
||||
order to support a minimal set of useful inter-event calculations.
|
||||
|
||||
These operations are needed for calculating latencies between events
|
||||
(timestamp1-timestamp0) and for combined latencies (latencies over 3
|
||||
or more events).
|
||||
|
||||
In the process, factor out some common code from key and value
|
||||
parsing.
|
||||
|
||||
Link: http://lkml.kernel.org/r/9a9308ead4fe32a433d9c7e95921fb798394f6b2.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
[kbuild test robot fix, add static to parse_atom()]
|
||||
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 26c5cb5e4790fec96e3eba02c347e78fa72273a8)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 487 ++++++++++++++++++++++++++-----
|
||||
1 file changed, 413 insertions(+), 74 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index c6c24d6d2be8..d2f01f3f26e1 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -32,6 +32,13 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
|
||||
#define HIST_FIELD_OPERANDS_MAX 2
|
||||
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
|
||||
|
||||
+enum field_op_id {
|
||||
+ FIELD_OP_NONE,
|
||||
+ FIELD_OP_PLUS,
|
||||
+ FIELD_OP_MINUS,
|
||||
+ FIELD_OP_UNARY_MINUS,
|
||||
+};
|
||||
+
|
||||
struct hist_var {
|
||||
char *name;
|
||||
struct hist_trigger_data *hist_data;
|
||||
@@ -48,6 +55,8 @@ struct hist_field {
|
||||
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
struct hist_trigger_data *hist_data;
|
||||
struct hist_var var;
|
||||
+ enum field_op_id operator;
|
||||
+ char *name;
|
||||
};
|
||||
|
||||
static u64 hist_field_none(struct hist_field *field, void *event,
|
||||
@@ -98,6 +107,41 @@ static u64 hist_field_log2(struct hist_field *hist_field, void *event,
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
|
||||
+static u64 hist_field_plus(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
+{
|
||||
+ struct hist_field *operand1 = hist_field->operands[0];
|
||||
+ struct hist_field *operand2 = hist_field->operands[1];
|
||||
+
|
||||
+ u64 val1 = operand1->fn(operand1, event, rbe);
|
||||
+ u64 val2 = operand2->fn(operand2, event, rbe);
|
||||
+
|
||||
+ return val1 + val2;
|
||||
+}
|
||||
+
|
||||
+static u64 hist_field_minus(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
+{
|
||||
+ struct hist_field *operand1 = hist_field->operands[0];
|
||||
+ struct hist_field *operand2 = hist_field->operands[1];
|
||||
+
|
||||
+ u64 val1 = operand1->fn(operand1, event, rbe);
|
||||
+ u64 val2 = operand2->fn(operand2, event, rbe);
|
||||
+
|
||||
+ return val1 - val2;
|
||||
+}
|
||||
+
|
||||
+static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event,
|
||||
+ struct ring_buffer_event *rbe)
|
||||
+{
|
||||
+ struct hist_field *operand = hist_field->operands[0];
|
||||
+
|
||||
+ s64 sval = (s64)operand->fn(operand, event, rbe);
|
||||
+ u64 val = (u64)-sval;
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
#define DEFINE_HIST_FIELD_FN(type) \
|
||||
static u64 hist_field_##type(struct hist_field *hist_field, \
|
||||
void *event, \
|
||||
@@ -147,6 +191,7 @@ enum hist_field_flags {
|
||||
HIST_FIELD_FL_TIMESTAMP = 1 << 10,
|
||||
HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11,
|
||||
HIST_FIELD_FL_VAR = 1 << 12,
|
||||
+ HIST_FIELD_FL_EXPR = 1 << 13,
|
||||
};
|
||||
|
||||
struct var_defs {
|
||||
@@ -258,6 +303,8 @@ static const char *hist_field_name(struct hist_field *field,
|
||||
field_name = hist_field_name(field->operands[0], ++level);
|
||||
else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
field_name = "common_timestamp";
|
||||
+ else if (field->flags & HIST_FIELD_FL_EXPR)
|
||||
+ field_name = field->name;
|
||||
|
||||
if (field_name == NULL)
|
||||
field_name = "";
|
||||
@@ -519,12 +566,104 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
return flags_str;
|
||||
}
|
||||
|
||||
+static void expr_field_str(struct hist_field *field, char *expr)
|
||||
+{
|
||||
+ strcat(expr, hist_field_name(field, 0));
|
||||
+
|
||||
+ if (field->flags) {
|
||||
+ const char *flags_str = get_hist_field_flags(field);
|
||||
+
|
||||
+ if (flags_str) {
|
||||
+ strcat(expr, ".");
|
||||
+ strcat(expr, flags_str);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static char *expr_str(struct hist_field *field, unsigned int level)
|
||||
+{
|
||||
+ char *expr;
|
||||
+
|
||||
+ if (level > 1)
|
||||
+ return NULL;
|
||||
+
|
||||
+ expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
|
||||
+ if (!expr)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (!field->operands[0]) {
|
||||
+ expr_field_str(field, expr);
|
||||
+ return expr;
|
||||
+ }
|
||||
+
|
||||
+ if (field->operator == FIELD_OP_UNARY_MINUS) {
|
||||
+ char *subexpr;
|
||||
+
|
||||
+ strcat(expr, "-(");
|
||||
+ subexpr = expr_str(field->operands[0], ++level);
|
||||
+ if (!subexpr) {
|
||||
+ kfree(expr);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ strcat(expr, subexpr);
|
||||
+ strcat(expr, ")");
|
||||
+
|
||||
+ kfree(subexpr);
|
||||
+
|
||||
+ return expr;
|
||||
+ }
|
||||
+
|
||||
+ expr_field_str(field->operands[0], expr);
|
||||
+
|
||||
+ switch (field->operator) {
|
||||
+ case FIELD_OP_MINUS:
|
||||
+ strcat(expr, "-");
|
||||
+ break;
|
||||
+ case FIELD_OP_PLUS:
|
||||
+ strcat(expr, "+");
|
||||
+ break;
|
||||
+ default:
|
||||
+ kfree(expr);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ expr_field_str(field->operands[1], expr);
|
||||
+
|
||||
+ return expr;
|
||||
+}
|
||||
+
|
||||
+static int contains_operator(char *str)
|
||||
+{
|
||||
+ enum field_op_id field_op = FIELD_OP_NONE;
|
||||
+ char *op;
|
||||
+
|
||||
+ op = strpbrk(str, "+-");
|
||||
+ if (!op)
|
||||
+ return FIELD_OP_NONE;
|
||||
+
|
||||
+ switch (*op) {
|
||||
+ case '-':
|
||||
+ if (*str == '-')
|
||||
+ field_op = FIELD_OP_UNARY_MINUS;
|
||||
+ else
|
||||
+ field_op = FIELD_OP_MINUS;
|
||||
+ break;
|
||||
+ case '+':
|
||||
+ field_op = FIELD_OP_PLUS;
|
||||
+ break;
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return field_op;
|
||||
+}
|
||||
+
|
||||
static void destroy_hist_field(struct hist_field *hist_field,
|
||||
unsigned int level)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
- if (level > 2)
|
||||
+ if (level > 3)
|
||||
return;
|
||||
|
||||
if (!hist_field)
|
||||
@@ -534,6 +673,7 @@ static void destroy_hist_field(struct hist_field *hist_field,
|
||||
destroy_hist_field(hist_field->operands[i], level + 1);
|
||||
|
||||
kfree(hist_field->var.name);
|
||||
+ kfree(hist_field->name);
|
||||
|
||||
kfree(hist_field);
|
||||
}
|
||||
@@ -554,6 +694,9 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
|
||||
hist_field->hist_data = hist_data;
|
||||
|
||||
+ if (flags & HIST_FIELD_FL_EXPR)
|
||||
+ goto out; /* caller will populate */
|
||||
+
|
||||
if (flags & HIST_FIELD_FL_HITCOUNT) {
|
||||
hist_field->fn = hist_field_counter;
|
||||
goto out;
|
||||
@@ -626,6 +769,257 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
}
|
||||
}
|
||||
|
||||
+static struct ftrace_event_field *
|
||||
+parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
|
||||
+ char *field_str, unsigned long *flags)
|
||||
+{
|
||||
+ struct ftrace_event_field *field = NULL;
|
||||
+ char *field_name, *modifier, *str;
|
||||
+
|
||||
+ modifier = str = kstrdup(field_str, GFP_KERNEL);
|
||||
+ if (!modifier)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ field_name = strsep(&modifier, ".");
|
||||
+ if (modifier) {
|
||||
+ if (strcmp(modifier, "hex") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_HEX;
|
||||
+ else if (strcmp(modifier, "sym") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_SYM;
|
||||
+ else if (strcmp(modifier, "sym-offset") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_SYM_OFFSET;
|
||||
+ else if ((strcmp(modifier, "execname") == 0) &&
|
||||
+ (strcmp(field_name, "common_pid") == 0))
|
||||
+ *flags |= HIST_FIELD_FL_EXECNAME;
|
||||
+ else if (strcmp(modifier, "syscall") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_SYSCALL;
|
||||
+ else if (strcmp(modifier, "log2") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_LOG2;
|
||||
+ else if (strcmp(modifier, "usecs") == 0)
|
||||
+ *flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
|
||||
+ else {
|
||||
+ field = ERR_PTR(-EINVAL);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
+ *flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
+ hist_data->enable_timestamps = true;
|
||||
+ if (*flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
+ hist_data->attrs->ts_in_usecs = true;
|
||||
+ } else {
|
||||
+ field = trace_find_event_field(file->event_call, field_name);
|
||||
+ if (!field || !field->size) {
|
||||
+ field = ERR_PTR(-EINVAL);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+ out:
|
||||
+ kfree(str);
|
||||
+
|
||||
+ return field;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file, char *str,
|
||||
+ unsigned long *flags, char *var_name)
|
||||
+{
|
||||
+ struct ftrace_event_field *field = NULL;
|
||||
+ struct hist_field *hist_field = NULL;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ field = parse_field(hist_data, file, str, flags);
|
||||
+ if (IS_ERR(field)) {
|
||||
+ ret = PTR_ERR(field);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ hist_field = create_hist_field(hist_data, field, *flags, var_name);
|
||||
+ if (!hist_field) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ return hist_field;
|
||||
+ out:
|
||||
+ return ERR_PTR(ret);
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *str, unsigned long flags,
|
||||
+ char *var_name, unsigned int level);
|
||||
+
|
||||
+static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *str, unsigned long flags,
|
||||
+ char *var_name, unsigned int level)
|
||||
+{
|
||||
+ struct hist_field *operand1, *expr = NULL;
|
||||
+ unsigned long operand_flags;
|
||||
+ int ret = 0;
|
||||
+ char *s;
|
||||
+
|
||||
+ // we support only -(xxx) i.e. explicit parens required
|
||||
+
|
||||
+ if (level > 3) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ str++; // skip leading '-'
|
||||
+
|
||||
+ s = strchr(str, '(');
|
||||
+ if (s)
|
||||
+ str++;
|
||||
+ else {
|
||||
+ ret = -EINVAL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ s = strrchr(str, ')');
|
||||
+ if (s)
|
||||
+ *s = '\0';
|
||||
+ else {
|
||||
+ ret = -EINVAL; // no closing ')'
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ flags |= HIST_FIELD_FL_EXPR;
|
||||
+ expr = create_hist_field(hist_data, NULL, flags, var_name);
|
||||
+ if (!expr) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ operand_flags = 0;
|
||||
+ operand1 = parse_expr(hist_data, file, str, operand_flags, NULL, ++level);
|
||||
+ if (IS_ERR(operand1)) {
|
||||
+ ret = PTR_ERR(operand1);
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ expr->flags |= operand1->flags &
|
||||
+ (HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
|
||||
+ expr->fn = hist_field_unary_minus;
|
||||
+ expr->operands[0] = operand1;
|
||||
+ expr->operator = FIELD_OP_UNARY_MINUS;
|
||||
+ expr->name = expr_str(expr, 0);
|
||||
+
|
||||
+ return expr;
|
||||
+ free:
|
||||
+ destroy_hist_field(expr, 0);
|
||||
+ return ERR_PTR(ret);
|
||||
+}
|
||||
+
|
||||
+static int check_expr_operands(struct hist_field *operand1,
|
||||
+ struct hist_field *operand2)
|
||||
+{
|
||||
+ unsigned long operand1_flags = operand1->flags;
|
||||
+ unsigned long operand2_flags = operand2->flags;
|
||||
+
|
||||
+ if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) !=
|
||||
+ (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file,
|
||||
+ char *str, unsigned long flags,
|
||||
+ char *var_name, unsigned int level)
|
||||
+{
|
||||
+ struct hist_field *operand1 = NULL, *operand2 = NULL, *expr = NULL;
|
||||
+ unsigned long operand_flags;
|
||||
+ int field_op, ret = -EINVAL;
|
||||
+ char *sep, *operand1_str;
|
||||
+
|
||||
+ if (level > 3)
|
||||
+ return ERR_PTR(-EINVAL);
|
||||
+
|
||||
+ field_op = contains_operator(str);
|
||||
+
|
||||
+ if (field_op == FIELD_OP_NONE)
|
||||
+ return parse_atom(hist_data, file, str, &flags, var_name);
|
||||
+
|
||||
+ if (field_op == FIELD_OP_UNARY_MINUS)
|
||||
+ return parse_unary(hist_data, file, str, flags, var_name, ++level);
|
||||
+
|
||||
+ switch (field_op) {
|
||||
+ case FIELD_OP_MINUS:
|
||||
+ sep = "-";
|
||||
+ break;
|
||||
+ case FIELD_OP_PLUS:
|
||||
+ sep = "+";
|
||||
+ break;
|
||||
+ default:
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ operand1_str = strsep(&str, sep);
|
||||
+ if (!operand1_str || !str)
|
||||
+ goto free;
|
||||
+
|
||||
+ operand_flags = 0;
|
||||
+ operand1 = parse_atom(hist_data, file, operand1_str,
|
||||
+ &operand_flags, NULL);
|
||||
+ if (IS_ERR(operand1)) {
|
||||
+ ret = PTR_ERR(operand1);
|
||||
+ operand1 = NULL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ // rest of string could be another expression e.g. b+c in a+b+c
|
||||
+ operand_flags = 0;
|
||||
+ operand2 = parse_expr(hist_data, file, str, operand_flags, NULL, ++level);
|
||||
+ if (IS_ERR(operand2)) {
|
||||
+ ret = PTR_ERR(operand2);
|
||||
+ operand2 = NULL;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ ret = check_expr_operands(operand1, operand2);
|
||||
+ if (ret)
|
||||
+ goto free;
|
||||
+
|
||||
+ flags |= HIST_FIELD_FL_EXPR;
|
||||
+
|
||||
+ flags |= operand1->flags &
|
||||
+ (HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
|
||||
+
|
||||
+ expr = create_hist_field(hist_data, NULL, flags, var_name);
|
||||
+ if (!expr) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ expr->operands[0] = operand1;
|
||||
+ expr->operands[1] = operand2;
|
||||
+ expr->operator = field_op;
|
||||
+ expr->name = expr_str(expr, 0);
|
||||
+
|
||||
+ switch (field_op) {
|
||||
+ case FIELD_OP_MINUS:
|
||||
+ expr->fn = hist_field_minus;
|
||||
+ break;
|
||||
+ case FIELD_OP_PLUS:
|
||||
+ expr->fn = hist_field_plus;
|
||||
+ break;
|
||||
+ default:
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ return expr;
|
||||
+ free:
|
||||
+ destroy_hist_field(operand1, 0);
|
||||
+ destroy_hist_field(operand2, 0);
|
||||
+ destroy_hist_field(expr, 0);
|
||||
+
|
||||
+ return ERR_PTR(ret);
|
||||
+}
|
||||
+
|
||||
static int create_hitcount_val(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
hist_data->fields[HITCOUNT_IDX] =
|
||||
@@ -648,37 +1042,17 @@ static int __create_val_field(struct hist_trigger_data *hist_data,
|
||||
char *var_name, char *field_str,
|
||||
unsigned long flags)
|
||||
{
|
||||
- struct ftrace_event_field *field = NULL;
|
||||
- char *field_name;
|
||||
+ struct hist_field *hist_field;
|
||||
int ret = 0;
|
||||
|
||||
- field_name = strsep(&field_str, ".");
|
||||
- if (field_str) {
|
||||
- if (strcmp(field_str, "hex") == 0)
|
||||
- flags |= HIST_FIELD_FL_HEX;
|
||||
- else {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
- flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
- hist_data->enable_timestamps = true;
|
||||
- } else {
|
||||
- field = trace_find_event_field(file->event_call, field_name);
|
||||
- if (!field || !field->size) {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- hist_data->fields[val_idx] = create_hist_field(hist_data, field, flags, var_name);
|
||||
- if (!hist_data->fields[val_idx]) {
|
||||
- ret = -ENOMEM;
|
||||
+ hist_field = parse_expr(hist_data, file, field_str, flags, var_name, 0);
|
||||
+ if (IS_ERR(hist_field)) {
|
||||
+ ret = PTR_ERR(hist_field);
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ hist_data->fields[val_idx] = hist_field;
|
||||
+
|
||||
++hist_data->n_vals;
|
||||
++hist_data->n_fields;
|
||||
|
||||
@@ -765,8 +1139,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
struct trace_event_file *file,
|
||||
char *field_str)
|
||||
{
|
||||
- struct ftrace_event_field *field = NULL;
|
||||
struct hist_field *hist_field = NULL;
|
||||
+
|
||||
unsigned long flags = 0;
|
||||
unsigned int key_size;
|
||||
int ret = 0;
|
||||
@@ -781,60 +1155,24 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
key_size = sizeof(unsigned long) * HIST_STACKTRACE_DEPTH;
|
||||
hist_field = create_hist_field(hist_data, NULL, flags, NULL);
|
||||
} else {
|
||||
- char *field_name = strsep(&field_str, ".");
|
||||
-
|
||||
- if (field_str) {
|
||||
- if (strcmp(field_str, "hex") == 0)
|
||||
- flags |= HIST_FIELD_FL_HEX;
|
||||
- else if (strcmp(field_str, "sym") == 0)
|
||||
- flags |= HIST_FIELD_FL_SYM;
|
||||
- else if (strcmp(field_str, "sym-offset") == 0)
|
||||
- flags |= HIST_FIELD_FL_SYM_OFFSET;
|
||||
- else if ((strcmp(field_str, "execname") == 0) &&
|
||||
- (strcmp(field_name, "common_pid") == 0))
|
||||
- flags |= HIST_FIELD_FL_EXECNAME;
|
||||
- else if (strcmp(field_str, "syscall") == 0)
|
||||
- flags |= HIST_FIELD_FL_SYSCALL;
|
||||
- else if (strcmp(field_str, "log2") == 0)
|
||||
- flags |= HIST_FIELD_FL_LOG2;
|
||||
- else if (strcmp(field_str, "usecs") == 0)
|
||||
- flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
|
||||
- else {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
+ hist_field = parse_expr(hist_data, file, field_str, flags,
|
||||
+ NULL, 0);
|
||||
+ if (IS_ERR(hist_field)) {
|
||||
+ ret = PTR_ERR(hist_field);
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
- if (strcmp(field_name, "common_timestamp") == 0) {
|
||||
- flags |= HIST_FIELD_FL_TIMESTAMP;
|
||||
- hist_data->enable_timestamps = true;
|
||||
- if (flags & HIST_FIELD_FL_TIMESTAMP_USECS)
|
||||
- hist_data->attrs->ts_in_usecs = true;
|
||||
- key_size = sizeof(u64);
|
||||
- } else {
|
||||
- field = trace_find_event_field(file->event_call, field_name);
|
||||
- if (!field || !field->size) {
|
||||
- ret = -EINVAL;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- if (is_string_field(field))
|
||||
- key_size = MAX_FILTER_STR_VAL;
|
||||
- else
|
||||
- key_size = field->size;
|
||||
- }
|
||||
+ key_size = hist_field->size;
|
||||
}
|
||||
|
||||
- hist_data->fields[key_idx] = create_hist_field(hist_data, field, flags, NULL);
|
||||
- if (!hist_data->fields[key_idx]) {
|
||||
- ret = -ENOMEM;
|
||||
- goto out;
|
||||
- }
|
||||
+ hist_data->fields[key_idx] = hist_field;
|
||||
|
||||
key_size = ALIGN(key_size, sizeof(u64));
|
||||
hist_data->fields[key_idx]->size = key_size;
|
||||
hist_data->fields[key_idx]->offset = key_offset;
|
||||
+
|
||||
hist_data->key_size += key_size;
|
||||
+
|
||||
if (hist_data->key_size > HIST_KEY_SIZE_MAX) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -1419,7 +1757,8 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
for (i = 1; i < hist_data->n_vals; i++) {
|
||||
field_name = hist_field_name(hist_data->fields[i], 0);
|
||||
|
||||
- if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR)
|
||||
+ if (hist_data->fields[i]->flags & HIST_FIELD_FL_VAR ||
|
||||
+ hist_data->fields[i]->flags & HIST_FIELD_FL_EXPR)
|
||||
continue;
|
||||
|
||||
if (hist_data->fields[i]->flags & HIST_FIELD_FL_HEX) {
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
From c3de8611575e0e51050210b128f9340bb006964a Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:53 -0600
|
||||
Subject: [PATCH 096/450] tracing: Generalize per-element hist trigger data
|
||||
|
||||
Up until now, hist triggers only needed per-element support for saving
|
||||
'comm' data, which was saved directly as a private data pointer.
|
||||
|
||||
In anticipation of the need to save other data besides 'comm', add a
|
||||
new hist_elt_data struct for the purpose, and switch the current
|
||||
'comm'-related code over to that.
|
||||
|
||||
Link: http://lkml.kernel.org/r/4502c338c965ddf5fc19fb1ec4764391e001ed4b.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 8102d0cb859d223564b17afb01e33701f57191d1)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 76 ++++++++++++++++++--------------
|
||||
1 file changed, 43 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index d2f01f3f26e1..893f65ce31a5 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -289,6 +289,10 @@ static struct hist_field *find_var(struct hist_trigger_data *hist_data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+struct hist_elt_data {
|
||||
+ char *comm;
|
||||
+};
|
||||
+
|
||||
static const char *hist_field_name(struct hist_field *field,
|
||||
unsigned int level)
|
||||
{
|
||||
@@ -503,45 +507,61 @@ static inline void save_comm(char *comm, struct task_struct *task)
|
||||
memcpy(comm, task->comm, TASK_COMM_LEN);
|
||||
}
|
||||
|
||||
-static void hist_trigger_elt_comm_free(struct tracing_map_elt *elt)
|
||||
+static void hist_elt_data_free(struct hist_elt_data *elt_data)
|
||||
+{
|
||||
+ kfree(elt_data->comm);
|
||||
+ kfree(elt_data);
|
||||
+}
|
||||
+
|
||||
+static void hist_trigger_elt_data_free(struct tracing_map_elt *elt)
|
||||
{
|
||||
- kfree((char *)elt->private_data);
|
||||
+ struct hist_elt_data *elt_data = elt->private_data;
|
||||
+
|
||||
+ hist_elt_data_free(elt_data);
|
||||
}
|
||||
|
||||
-static int hist_trigger_elt_comm_alloc(struct tracing_map_elt *elt)
|
||||
+static int hist_trigger_elt_data_alloc(struct tracing_map_elt *elt)
|
||||
{
|
||||
struct hist_trigger_data *hist_data = elt->map->private_data;
|
||||
+ unsigned int size = TASK_COMM_LEN;
|
||||
+ struct hist_elt_data *elt_data;
|
||||
struct hist_field *key_field;
|
||||
unsigned int i;
|
||||
|
||||
+ elt_data = kzalloc(sizeof(*elt_data), GFP_KERNEL);
|
||||
+ if (!elt_data)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
for_each_hist_key_field(i, hist_data) {
|
||||
key_field = hist_data->fields[i];
|
||||
|
||||
if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
|
||||
- unsigned int size = TASK_COMM_LEN + 1;
|
||||
-
|
||||
- elt->private_data = kzalloc(size, GFP_KERNEL);
|
||||
- if (!elt->private_data)
|
||||
+ elt_data->comm = kzalloc(size, GFP_KERNEL);
|
||||
+ if (!elt_data->comm) {
|
||||
+ kfree(elt_data);
|
||||
return -ENOMEM;
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+ elt->private_data = elt_data;
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void hist_trigger_elt_comm_init(struct tracing_map_elt *elt)
|
||||
+static void hist_trigger_elt_data_init(struct tracing_map_elt *elt)
|
||||
{
|
||||
- char *comm = elt->private_data;
|
||||
+ struct hist_elt_data *elt_data = elt->private_data;
|
||||
|
||||
- if (comm)
|
||||
- save_comm(comm, current);
|
||||
+ if (elt_data->comm)
|
||||
+ save_comm(elt_data->comm, current);
|
||||
}
|
||||
|
||||
-static const struct tracing_map_ops hist_trigger_elt_comm_ops = {
|
||||
- .elt_alloc = hist_trigger_elt_comm_alloc,
|
||||
- .elt_free = hist_trigger_elt_comm_free,
|
||||
- .elt_init = hist_trigger_elt_comm_init,
|
||||
+static const struct tracing_map_ops hist_trigger_elt_data_ops = {
|
||||
+ .elt_alloc = hist_trigger_elt_data_alloc,
|
||||
+ .elt_free = hist_trigger_elt_data_free,
|
||||
+ .elt_init = hist_trigger_elt_data_init,
|
||||
};
|
||||
|
||||
static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
@@ -1484,21 +1504,6 @@ static int create_tracing_map_fields(struct hist_trigger_data *hist_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static bool need_tracing_map_ops(struct hist_trigger_data *hist_data)
|
||||
-{
|
||||
- struct hist_field *key_field;
|
||||
- unsigned int i;
|
||||
-
|
||||
- for_each_hist_key_field(i, hist_data) {
|
||||
- key_field = hist_data->fields[i];
|
||||
-
|
||||
- if (key_field->flags & HIST_FIELD_FL_EXECNAME)
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
static struct hist_trigger_data *
|
||||
create_hist_data(unsigned int map_bits,
|
||||
struct hist_trigger_attrs *attrs,
|
||||
@@ -1524,8 +1529,7 @@ create_hist_data(unsigned int map_bits,
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
- if (need_tracing_map_ops(hist_data))
|
||||
- map_ops = &hist_trigger_elt_comm_ops;
|
||||
+ map_ops = &hist_trigger_elt_data_ops;
|
||||
|
||||
hist_data->map = tracing_map_create(map_bits, hist_data->key_size,
|
||||
map_ops, hist_data);
|
||||
@@ -1713,7 +1717,13 @@ hist_trigger_entry_print(struct seq_file *m,
|
||||
seq_printf(m, "%s: [%llx] %-55s", field_name,
|
||||
uval, str);
|
||||
} else if (key_field->flags & HIST_FIELD_FL_EXECNAME) {
|
||||
- char *comm = elt->private_data;
|
||||
+ struct hist_elt_data *elt_data = elt->private_data;
|
||||
+ char *comm;
|
||||
+
|
||||
+ if (WARN_ON_ONCE(!elt_data))
|
||||
+ return;
|
||||
+
|
||||
+ comm = elt_data->comm;
|
||||
|
||||
uval = *(u64 *)(key + key_field->offset);
|
||||
seq_printf(m, "%s: %-16s[%10llu]", field_name,
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
From d2ce1d4ffafada6db4b9b4f8fe5561f40cb03b76 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:54 -0600
|
||||
Subject: [PATCH 097/450] tracing: Pass tracing_map_elt to hist_field accessor
|
||||
functions
|
||||
|
||||
Some accessor functions, such as for variable references, require
|
||||
access to a corrsponding tracing_map_elt.
|
||||
|
||||
Add a tracing_map_elt param to the function signature and update the
|
||||
accessor functions accordingly.
|
||||
|
||||
Link: http://lkml.kernel.org/r/e0f292b068e9e4948da1d5af21b5ae0efa9b5717.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 8405bbbbc9dc0d88ffc92848cb8f0bda2c7a1b30)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 91 ++++++++++++++++++++------------
|
||||
1 file changed, 57 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 893f65ce31a5..becf37b2b0da 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -26,8 +26,10 @@
|
||||
|
||||
struct hist_field;
|
||||
|
||||
-typedef u64 (*hist_field_fn_t) (struct hist_field *field, void *event,
|
||||
- struct ring_buffer_event *rbe);
|
||||
+typedef u64 (*hist_field_fn_t) (struct hist_field *field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event);
|
||||
|
||||
#define HIST_FIELD_OPERANDS_MAX 2
|
||||
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
|
||||
@@ -59,28 +61,36 @@ struct hist_field {
|
||||
char *name;
|
||||
};
|
||||
|
||||
-static u64 hist_field_none(struct hist_field *field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_none(struct hist_field *field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static u64 hist_field_counter(struct hist_field *field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_counter(struct hist_field *field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
-static u64 hist_field_string(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_string(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
char *addr = (char *)(event + hist_field->field->offset);
|
||||
|
||||
return (u64)(unsigned long)addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_dynstring(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
u32 str_item = *(u32 *)(event + hist_field->field->offset);
|
||||
int str_loc = str_item & 0xffff;
|
||||
@@ -89,54 +99,64 @@ static u64 hist_field_dynstring(struct hist_field *hist_field, void *event,
|
||||
return (u64)(unsigned long)addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_pstring(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_pstring(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
char **addr = (char **)(event + hist_field->field->offset);
|
||||
|
||||
return (u64)(unsigned long)*addr;
|
||||
}
|
||||
|
||||
-static u64 hist_field_log2(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_log2(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
struct hist_field *operand = hist_field->operands[0];
|
||||
|
||||
- u64 val = operand->fn(operand, event, rbe);
|
||||
+ u64 val = operand->fn(operand, elt, rbe, event);
|
||||
|
||||
return (u64) ilog2(roundup_pow_of_two(val));
|
||||
}
|
||||
|
||||
-static u64 hist_field_plus(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_plus(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
struct hist_field *operand1 = hist_field->operands[0];
|
||||
struct hist_field *operand2 = hist_field->operands[1];
|
||||
|
||||
- u64 val1 = operand1->fn(operand1, event, rbe);
|
||||
- u64 val2 = operand2->fn(operand2, event, rbe);
|
||||
+ u64 val1 = operand1->fn(operand1, elt, rbe, event);
|
||||
+ u64 val2 = operand2->fn(operand2, elt, rbe, event);
|
||||
|
||||
return val1 + val2;
|
||||
}
|
||||
|
||||
-static u64 hist_field_minus(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_minus(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
struct hist_field *operand1 = hist_field->operands[0];
|
||||
struct hist_field *operand2 = hist_field->operands[1];
|
||||
|
||||
- u64 val1 = operand1->fn(operand1, event, rbe);
|
||||
- u64 val2 = operand2->fn(operand2, event, rbe);
|
||||
+ u64 val1 = operand1->fn(operand1, elt, rbe, event);
|
||||
+ u64 val2 = operand2->fn(operand2, elt, rbe, event);
|
||||
|
||||
return val1 - val2;
|
||||
}
|
||||
|
||||
-static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_unary_minus(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
struct hist_field *operand = hist_field->operands[0];
|
||||
|
||||
- s64 sval = (s64)operand->fn(operand, event, rbe);
|
||||
+ s64 sval = (s64)operand->fn(operand, elt, rbe, event);
|
||||
u64 val = (u64)-sval;
|
||||
|
||||
return val;
|
||||
@@ -144,8 +164,9 @@ static u64 hist_field_unary_minus(struct hist_field *hist_field, void *event,
|
||||
|
||||
#define DEFINE_HIST_FIELD_FN(type) \
|
||||
static u64 hist_field_##type(struct hist_field *hist_field, \
|
||||
- void *event, \
|
||||
- struct ring_buffer_event *rbe) \
|
||||
+ struct tracing_map_elt *elt, \
|
||||
+ struct ring_buffer_event *rbe, \
|
||||
+ void *event) \
|
||||
{ \
|
||||
type *addr = (type *)(event + hist_field->field->offset); \
|
||||
\
|
||||
@@ -233,8 +254,10 @@ struct hist_trigger_data {
|
||||
bool remove;
|
||||
};
|
||||
|
||||
-static u64 hist_field_timestamp(struct hist_field *hist_field, void *event,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+static u64 hist_field_timestamp(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
{
|
||||
struct hist_trigger_data *hist_data = hist_field->hist_data;
|
||||
struct trace_array *tr = hist_data->event_file->tr;
|
||||
@@ -1570,7 +1593,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
|
||||
for_each_hist_val_field(i, hist_data) {
|
||||
hist_field = hist_data->fields[i];
|
||||
- hist_val = hist_field->fn(hist_field, rec, rbe);
|
||||
+ hist_val = hist_field->fn(hist_field, elt, rbe, rec);
|
||||
if (hist_field->flags & HIST_FIELD_FL_VAR) {
|
||||
var_idx = hist_field->var.idx;
|
||||
tracing_map_set_var(elt, var_idx, hist_val);
|
||||
@@ -1582,7 +1605,7 @@ static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
for_each_hist_key_field(i, hist_data) {
|
||||
hist_field = hist_data->fields[i];
|
||||
if (hist_field->flags & HIST_FIELD_FL_VAR) {
|
||||
- hist_val = hist_field->fn(hist_field, rec, rbe);
|
||||
+ hist_val = hist_field->fn(hist_field, elt, rbe, rec);
|
||||
var_idx = hist_field->var.idx;
|
||||
tracing_map_set_var(elt, var_idx, hist_val);
|
||||
}
|
||||
@@ -1620,9 +1643,9 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
bool use_compound_key = (hist_data->n_keys > 1);
|
||||
unsigned long entries[HIST_STACKTRACE_DEPTH];
|
||||
char compound_key[HIST_KEY_SIZE_MAX];
|
||||
+ struct tracing_map_elt *elt = NULL;
|
||||
struct stack_trace stacktrace;
|
||||
struct hist_field *key_field;
|
||||
- struct tracing_map_elt *elt;
|
||||
u64 field_contents;
|
||||
void *key = NULL;
|
||||
unsigned int i;
|
||||
@@ -1643,7 +1666,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
|
||||
key = entries;
|
||||
} else {
|
||||
- field_contents = key_field->fn(key_field, rec, rbe);
|
||||
+ field_contents = key_field->fn(key_field, elt, rbe, rec);
|
||||
if (key_field->flags & HIST_FIELD_FL_STRING) {
|
||||
key = (void *)(unsigned long)field_contents;
|
||||
use_compound_key = true;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
From d333c65c795cde2de88f5759101bc29c36058992 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:55 -0600
|
||||
Subject: [PATCH 098/450] tracing: Add hist_field 'type' field
|
||||
|
||||
Future support for synthetic events requires hist_field 'type'
|
||||
information, so add a field for that.
|
||||
|
||||
Also, make other hist_field attribute usage consistent (size,
|
||||
is_signed, etc).
|
||||
|
||||
Link: http://lkml.kernel.org/r/3fd12a2e86316b05151ba0d7c68268e780af2c9d.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit d544a468f82526e97cc80c18a019708eb203b00a)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 33 ++++++++++++++++++++++++++++++++
|
||||
1 file changed, 33 insertions(+)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index becf37b2b0da..6e60cb794dcd 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -54,6 +54,7 @@ struct hist_field {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
unsigned int is_signed;
|
||||
+ const char *type;
|
||||
struct hist_field *operands[HIST_FIELD_OPERANDS_MAX];
|
||||
struct hist_trigger_data *hist_data;
|
||||
struct hist_var var;
|
||||
@@ -717,6 +718,7 @@ static void destroy_hist_field(struct hist_field *hist_field,
|
||||
|
||||
kfree(hist_field->var.name);
|
||||
kfree(hist_field->name);
|
||||
+ kfree(hist_field->type);
|
||||
|
||||
kfree(hist_field);
|
||||
}
|
||||
@@ -742,6 +744,10 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
|
||||
if (flags & HIST_FIELD_FL_HITCOUNT) {
|
||||
hist_field->fn = hist_field_counter;
|
||||
+ hist_field->size = sizeof(u64);
|
||||
+ hist_field->type = kstrdup("u64", GFP_KERNEL);
|
||||
+ if (!hist_field->type)
|
||||
+ goto free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -755,12 +761,18 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
hist_field->fn = hist_field_log2;
|
||||
hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
|
||||
hist_field->size = hist_field->operands[0]->size;
|
||||
+ hist_field->type = kstrdup(hist_field->operands[0]->type, GFP_KERNEL);
|
||||
+ if (!hist_field->type)
|
||||
+ goto free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (flags & HIST_FIELD_FL_TIMESTAMP) {
|
||||
hist_field->fn = hist_field_timestamp;
|
||||
hist_field->size = sizeof(u64);
|
||||
+ hist_field->type = kstrdup("u64", GFP_KERNEL);
|
||||
+ if (!hist_field->type)
|
||||
+ goto free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -770,6 +782,11 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
if (is_string_field(field)) {
|
||||
flags |= HIST_FIELD_FL_STRING;
|
||||
|
||||
+ hist_field->size = MAX_FILTER_STR_VAL;
|
||||
+ hist_field->type = kstrdup(field->type, GFP_KERNEL);
|
||||
+ if (!hist_field->type)
|
||||
+ goto free;
|
||||
+
|
||||
if (field->filter_type == FILTER_STATIC_STRING)
|
||||
hist_field->fn = hist_field_string;
|
||||
else if (field->filter_type == FILTER_DYN_STRING)
|
||||
@@ -777,6 +794,12 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
else
|
||||
hist_field->fn = hist_field_pstring;
|
||||
} else {
|
||||
+ hist_field->size = field->size;
|
||||
+ hist_field->is_signed = field->is_signed;
|
||||
+ hist_field->type = kstrdup(field->type, GFP_KERNEL);
|
||||
+ if (!hist_field->type)
|
||||
+ goto free;
|
||||
+
|
||||
hist_field->fn = select_value_fn(field->size,
|
||||
field->is_signed);
|
||||
if (!hist_field->fn) {
|
||||
@@ -949,6 +972,11 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
|
||||
expr->operands[0] = operand1;
|
||||
expr->operator = FIELD_OP_UNARY_MINUS;
|
||||
expr->name = expr_str(expr, 0);
|
||||
+ expr->type = kstrdup(operand1->type, GFP_KERNEL);
|
||||
+ if (!expr->type) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
|
||||
return expr;
|
||||
free:
|
||||
@@ -1042,6 +1070,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
expr->operands[1] = operand2;
|
||||
expr->operator = field_op;
|
||||
expr->name = expr_str(expr, 0);
|
||||
+ expr->type = kstrdup(operand1->type, GFP_KERNEL);
|
||||
+ if (!expr->type) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
|
||||
switch (field_op) {
|
||||
case FIELD_OP_MINUS:
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,967 +0,0 @@
|
||||
From ba702b3dad3b6d3b50caac695d99bf3e30e34199 Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:56 -0600
|
||||
Subject: [PATCH 099/450] tracing: Add variable reference handling to hist
|
||||
triggers
|
||||
|
||||
Add the necessary infrastructure to allow the variables defined on one
|
||||
event to be referenced in another. This allows variables set by a
|
||||
previous event to be referenced and used in expressions combining the
|
||||
variable values saved by that previous event and the event fields of
|
||||
the current event. For example, here's how a latency can be
|
||||
calculated and saved into yet another variable named 'wakeup_lat':
|
||||
|
||||
# echo 'hist:keys=pid,prio:ts0=common_timestamp ...
|
||||
# echo 'hist:keys=next_pid:wakeup_lat=common_timestamp-$ts0 ...
|
||||
|
||||
In the first event, the event's timetamp is saved into the variable
|
||||
ts0. In the next line, ts0 is subtracted from the second event's
|
||||
timestamp to produce the latency.
|
||||
|
||||
Further users of variable references will be described in subsequent
|
||||
patches, such as for instance how the 'wakeup_lat' variable above can
|
||||
be displayed in a latency histogram.
|
||||
|
||||
Link: http://lkml.kernel.org/r/b1d3e6975374e34d501ff417c20189c3f9b2c7b8.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit 434c1d5831194e72e6eb30d46534d75b5a985eb7)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace.c | 2 +
|
||||
kernel/trace/trace.h | 3 +
|
||||
kernel/trace/trace_events_hist.c | 661 +++++++++++++++++++++++++++-
|
||||
kernel/trace/trace_events_trigger.c | 6 +
|
||||
4 files changed, 656 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
|
||||
index 1f695da375df..5b35e4257cc5 100644
|
||||
--- a/kernel/trace/trace.c
|
||||
+++ b/kernel/trace/trace.c
|
||||
@@ -7801,6 +7801,7 @@ static int instance_mkdir(const char *name)
|
||||
|
||||
INIT_LIST_HEAD(&tr->systems);
|
||||
INIT_LIST_HEAD(&tr->events);
|
||||
+ INIT_LIST_HEAD(&tr->hist_vars);
|
||||
|
||||
if (allocate_trace_buffers(tr, trace_buf_size) < 0)
|
||||
goto out_free_tr;
|
||||
@@ -8553,6 +8554,7 @@ __init static int tracer_alloc_buffers(void)
|
||||
|
||||
INIT_LIST_HEAD(&global_trace.systems);
|
||||
INIT_LIST_HEAD(&global_trace.events);
|
||||
+ INIT_LIST_HEAD(&global_trace.hist_vars);
|
||||
list_add(&global_trace.list, &ftrace_trace_arrays);
|
||||
|
||||
apply_trace_boot_options();
|
||||
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
|
||||
index 92936a6fc29e..5975d5f5c4bc 100644
|
||||
--- a/kernel/trace/trace.h
|
||||
+++ b/kernel/trace/trace.h
|
||||
@@ -274,6 +274,7 @@ struct trace_array {
|
||||
int function_enabled;
|
||||
#endif
|
||||
int time_stamp_abs_ref;
|
||||
+ struct list_head hist_vars;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -1550,6 +1551,8 @@ extern void pause_named_trigger(struct event_trigger_data *data);
|
||||
extern void unpause_named_trigger(struct event_trigger_data *data);
|
||||
extern void set_named_trigger_data(struct event_trigger_data *data,
|
||||
struct event_trigger_data *named_data);
|
||||
+extern struct event_trigger_data *
|
||||
+get_named_trigger_data(struct event_trigger_data *data);
|
||||
extern int register_event_command(struct event_command *cmd);
|
||||
extern int unregister_event_command(struct event_command *cmd);
|
||||
extern int register_trigger_hist_enable_disable_cmds(void);
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 6e60cb794dcd..2bc1f76915ef 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -59,7 +59,12 @@ struct hist_field {
|
||||
struct hist_trigger_data *hist_data;
|
||||
struct hist_var var;
|
||||
enum field_op_id operator;
|
||||
+ char *system;
|
||||
+ char *event_name;
|
||||
char *name;
|
||||
+ unsigned int var_idx;
|
||||
+ unsigned int var_ref_idx;
|
||||
+ bool read_once;
|
||||
};
|
||||
|
||||
static u64 hist_field_none(struct hist_field *field,
|
||||
@@ -214,6 +219,7 @@ enum hist_field_flags {
|
||||
HIST_FIELD_FL_TIMESTAMP_USECS = 1 << 11,
|
||||
HIST_FIELD_FL_VAR = 1 << 12,
|
||||
HIST_FIELD_FL_EXPR = 1 << 13,
|
||||
+ HIST_FIELD_FL_VAR_REF = 1 << 14,
|
||||
};
|
||||
|
||||
struct var_defs {
|
||||
@@ -253,6 +259,8 @@ struct hist_trigger_data {
|
||||
struct tracing_map *map;
|
||||
bool enable_timestamps;
|
||||
bool remove;
|
||||
+ struct hist_field *var_refs[TRACING_MAP_VARS_MAX];
|
||||
+ unsigned int n_var_refs;
|
||||
};
|
||||
|
||||
static u64 hist_field_timestamp(struct hist_field *hist_field,
|
||||
@@ -271,6 +279,214 @@ static u64 hist_field_timestamp(struct hist_field *hist_field,
|
||||
return ts;
|
||||
}
|
||||
|
||||
+struct hist_var_data {
|
||||
+ struct list_head list;
|
||||
+ struct hist_trigger_data *hist_data;
|
||||
+};
|
||||
+
|
||||
+static struct hist_field *
|
||||
+check_field_for_var_ref(struct hist_field *hist_field,
|
||||
+ struct hist_trigger_data *var_data,
|
||||
+ unsigned int var_idx)
|
||||
+{
|
||||
+ struct hist_field *found = NULL;
|
||||
+
|
||||
+ if (hist_field && hist_field->flags & HIST_FIELD_FL_VAR_REF) {
|
||||
+ if (hist_field->var.idx == var_idx &&
|
||||
+ hist_field->var.hist_data == var_data) {
|
||||
+ found = hist_field;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *
|
||||
+check_field_for_var_refs(struct hist_trigger_data *hist_data,
|
||||
+ struct hist_field *hist_field,
|
||||
+ struct hist_trigger_data *var_data,
|
||||
+ unsigned int var_idx,
|
||||
+ unsigned int level)
|
||||
+{
|
||||
+ struct hist_field *found = NULL;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ if (level > 3)
|
||||
+ return found;
|
||||
+
|
||||
+ if (!hist_field)
|
||||
+ return found;
|
||||
+
|
||||
+ found = check_field_for_var_ref(hist_field, var_data, var_idx);
|
||||
+ if (found)
|
||||
+ return found;
|
||||
+
|
||||
+ for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) {
|
||||
+ struct hist_field *operand;
|
||||
+
|
||||
+ operand = hist_field->operands[i];
|
||||
+ found = check_field_for_var_refs(hist_data, operand, var_data,
|
||||
+ var_idx, level + 1);
|
||||
+ if (found)
|
||||
+ return found;
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *find_var_ref(struct hist_trigger_data *hist_data,
|
||||
+ struct hist_trigger_data *var_data,
|
||||
+ unsigned int var_idx)
|
||||
+{
|
||||
+ struct hist_field *hist_field, *found = NULL;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for_each_hist_field(i, hist_data) {
|
||||
+ hist_field = hist_data->fields[i];
|
||||
+ found = check_field_for_var_refs(hist_data, hist_field,
|
||||
+ var_data, var_idx, 0);
|
||||
+ if (found)
|
||||
+ return found;
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *find_any_var_ref(struct hist_trigger_data *hist_data,
|
||||
+ unsigned int var_idx)
|
||||
+{
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+ struct hist_field *found = NULL;
|
||||
+ struct hist_var_data *var_data;
|
||||
+
|
||||
+ list_for_each_entry(var_data, &tr->hist_vars, list) {
|
||||
+ if (var_data->hist_data == hist_data)
|
||||
+ continue;
|
||||
+ found = find_var_ref(var_data->hist_data, hist_data, var_idx);
|
||||
+ if (found)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static bool check_var_refs(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ struct hist_field *field;
|
||||
+ bool found = false;
|
||||
+ int i;
|
||||
+
|
||||
+ for_each_hist_field(i, hist_data) {
|
||||
+ field = hist_data->fields[i];
|
||||
+ if (field && field->flags & HIST_FIELD_FL_VAR) {
|
||||
+ if (find_any_var_ref(hist_data, field->var.idx)) {
|
||||
+ found = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_var_data *find_hist_vars(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+ struct hist_var_data *var_data, *found = NULL;
|
||||
+
|
||||
+ list_for_each_entry(var_data, &tr->hist_vars, list) {
|
||||
+ if (var_data->hist_data == hist_data) {
|
||||
+ found = var_data;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static bool field_has_hist_vars(struct hist_field *hist_field,
|
||||
+ unsigned int level)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (level > 3)
|
||||
+ return false;
|
||||
+
|
||||
+ if (!hist_field)
|
||||
+ return false;
|
||||
+
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR ||
|
||||
+ hist_field->flags & HIST_FIELD_FL_VAR_REF)
|
||||
+ return true;
|
||||
+
|
||||
+ for (i = 0; i < HIST_FIELD_OPERANDS_MAX; i++) {
|
||||
+ struct hist_field *operand;
|
||||
+
|
||||
+ operand = hist_field->operands[i];
|
||||
+ if (field_has_hist_vars(operand, level + 1))
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static bool has_hist_vars(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ struct hist_field *hist_field;
|
||||
+ int i;
|
||||
+
|
||||
+ for_each_hist_field(i, hist_data) {
|
||||
+ hist_field = hist_data->fields[i];
|
||||
+ if (field_has_hist_vars(hist_field, 0))
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static int save_hist_vars(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+ struct hist_var_data *var_data;
|
||||
+
|
||||
+ var_data = find_hist_vars(hist_data);
|
||||
+ if (var_data)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (trace_array_get(tr) < 0)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ var_data = kzalloc(sizeof(*var_data), GFP_KERNEL);
|
||||
+ if (!var_data) {
|
||||
+ trace_array_put(tr);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ var_data->hist_data = hist_data;
|
||||
+ list_add(&var_data->list, &tr->hist_vars);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void remove_hist_vars(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+ struct hist_var_data *var_data;
|
||||
+
|
||||
+ var_data = find_hist_vars(hist_data);
|
||||
+ if (!var_data)
|
||||
+ return;
|
||||
+
|
||||
+ if (WARN_ON(check_var_refs(hist_data)))
|
||||
+ return;
|
||||
+
|
||||
+ list_del(&var_data->list);
|
||||
+
|
||||
+ kfree(var_data);
|
||||
+
|
||||
+ trace_array_put(tr);
|
||||
+}
|
||||
+
|
||||
static struct hist_field *find_var_field(struct hist_trigger_data *hist_data,
|
||||
const char *var_name)
|
||||
{
|
||||
@@ -313,10 +529,137 @@ static struct hist_field *find_var(struct hist_trigger_data *hist_data,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+static struct trace_event_file *find_var_file(struct trace_array *tr,
|
||||
+ char *system,
|
||||
+ char *event_name,
|
||||
+ char *var_name)
|
||||
+{
|
||||
+ struct hist_trigger_data *var_hist_data;
|
||||
+ struct hist_var_data *var_data;
|
||||
+ struct trace_event_file *file, *found = NULL;
|
||||
+
|
||||
+ if (system)
|
||||
+ return find_event_file(tr, system, event_name);
|
||||
+
|
||||
+ list_for_each_entry(var_data, &tr->hist_vars, list) {
|
||||
+ var_hist_data = var_data->hist_data;
|
||||
+ file = var_hist_data->event_file;
|
||||
+ if (file == found)
|
||||
+ continue;
|
||||
+
|
||||
+ if (find_var_field(var_hist_data, var_name)) {
|
||||
+ if (found)
|
||||
+ return NULL;
|
||||
+
|
||||
+ found = file;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return found;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *find_file_var(struct trace_event_file *file,
|
||||
+ const char *var_name)
|
||||
+{
|
||||
+ struct hist_trigger_data *test_data;
|
||||
+ struct event_trigger_data *test;
|
||||
+ struct hist_field *hist_field;
|
||||
+
|
||||
+ list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
+ test_data = test->private_data;
|
||||
+ hist_field = find_var_field(test_data, var_name);
|
||||
+ if (hist_field)
|
||||
+ return hist_field;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *find_event_var(struct hist_trigger_data *hist_data,
|
||||
+ char *system,
|
||||
+ char *event_name,
|
||||
+ char *var_name)
|
||||
+{
|
||||
+ struct trace_array *tr = hist_data->event_file->tr;
|
||||
+ struct hist_field *hist_field = NULL;
|
||||
+ struct trace_event_file *file;
|
||||
+
|
||||
+ file = find_var_file(tr, system, event_name, var_name);
|
||||
+ if (!file)
|
||||
+ return NULL;
|
||||
+
|
||||
+ hist_field = find_file_var(file, var_name);
|
||||
+
|
||||
+ return hist_field;
|
||||
+}
|
||||
+
|
||||
struct hist_elt_data {
|
||||
char *comm;
|
||||
+ u64 *var_ref_vals;
|
||||
};
|
||||
|
||||
+static u64 hist_field_var_ref(struct hist_field *hist_field,
|
||||
+ struct tracing_map_elt *elt,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ void *event)
|
||||
+{
|
||||
+ struct hist_elt_data *elt_data;
|
||||
+ u64 var_val = 0;
|
||||
+
|
||||
+ elt_data = elt->private_data;
|
||||
+ var_val = elt_data->var_ref_vals[hist_field->var_ref_idx];
|
||||
+
|
||||
+ return var_val;
|
||||
+}
|
||||
+
|
||||
+static bool resolve_var_refs(struct hist_trigger_data *hist_data, void *key,
|
||||
+ u64 *var_ref_vals, bool self)
|
||||
+{
|
||||
+ struct hist_trigger_data *var_data;
|
||||
+ struct tracing_map_elt *var_elt;
|
||||
+ struct hist_field *hist_field;
|
||||
+ unsigned int i, var_idx;
|
||||
+ bool resolved = true;
|
||||
+ u64 var_val = 0;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->n_var_refs; i++) {
|
||||
+ hist_field = hist_data->var_refs[i];
|
||||
+ var_idx = hist_field->var.idx;
|
||||
+ var_data = hist_field->var.hist_data;
|
||||
+
|
||||
+ if (var_data == NULL) {
|
||||
+ resolved = false;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if ((self && var_data != hist_data) ||
|
||||
+ (!self && var_data == hist_data))
|
||||
+ continue;
|
||||
+
|
||||
+ var_elt = tracing_map_lookup(var_data->map, key);
|
||||
+ if (!var_elt) {
|
||||
+ resolved = false;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (!tracing_map_var_set(var_elt, var_idx)) {
|
||||
+ resolved = false;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (self || !hist_field->read_once)
|
||||
+ var_val = tracing_map_read_var(var_elt, var_idx);
|
||||
+ else
|
||||
+ var_val = tracing_map_read_var_once(var_elt, var_idx);
|
||||
+
|
||||
+ var_ref_vals[i] = var_val;
|
||||
+ }
|
||||
+
|
||||
+ return resolved;
|
||||
+}
|
||||
+
|
||||
static const char *hist_field_name(struct hist_field *field,
|
||||
unsigned int level)
|
||||
{
|
||||
@@ -331,8 +674,20 @@ static const char *hist_field_name(struct hist_field *field,
|
||||
field_name = hist_field_name(field->operands[0], ++level);
|
||||
else if (field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
field_name = "common_timestamp";
|
||||
- else if (field->flags & HIST_FIELD_FL_EXPR)
|
||||
- field_name = field->name;
|
||||
+ else if (field->flags & HIST_FIELD_FL_EXPR ||
|
||||
+ field->flags & HIST_FIELD_FL_VAR_REF) {
|
||||
+ if (field->system) {
|
||||
+ static char full_name[MAX_FILTER_STR_VAL];
|
||||
+
|
||||
+ strcat(full_name, field->system);
|
||||
+ strcat(full_name, ".");
|
||||
+ strcat(full_name, field->event_name);
|
||||
+ strcat(full_name, ".");
|
||||
+ strcat(full_name, field->name);
|
||||
+ field_name = full_name;
|
||||
+ } else
|
||||
+ field_name = field->name;
|
||||
+ }
|
||||
|
||||
if (field_name == NULL)
|
||||
field_name = "";
|
||||
@@ -612,6 +967,9 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
|
||||
|
||||
static void expr_field_str(struct hist_field *field, char *expr)
|
||||
{
|
||||
+ if (field->flags & HIST_FIELD_FL_VAR_REF)
|
||||
+ strcat(expr, "$");
|
||||
+
|
||||
strcat(expr, hist_field_name(field, 0));
|
||||
|
||||
if (field->flags) {
|
||||
@@ -742,6 +1100,11 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
|
||||
if (flags & HIST_FIELD_FL_EXPR)
|
||||
goto out; /* caller will populate */
|
||||
|
||||
+ if (flags & HIST_FIELD_FL_VAR_REF) {
|
||||
+ hist_field->fn = hist_field_var_ref;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
if (flags & HIST_FIELD_FL_HITCOUNT) {
|
||||
hist_field->fn = hist_field_counter;
|
||||
hist_field->size = sizeof(u64);
|
||||
@@ -835,6 +1198,144 @@ static void destroy_hist_fields(struct hist_trigger_data *hist_data)
|
||||
}
|
||||
}
|
||||
|
||||
+static int init_var_ref(struct hist_field *ref_field,
|
||||
+ struct hist_field *var_field,
|
||||
+ char *system, char *event_name)
|
||||
+{
|
||||
+ int err = 0;
|
||||
+
|
||||
+ ref_field->var.idx = var_field->var.idx;
|
||||
+ ref_field->var.hist_data = var_field->hist_data;
|
||||
+ ref_field->size = var_field->size;
|
||||
+ ref_field->is_signed = var_field->is_signed;
|
||||
+ ref_field->flags |= var_field->flags &
|
||||
+ (HIST_FIELD_FL_TIMESTAMP | HIST_FIELD_FL_TIMESTAMP_USECS);
|
||||
+
|
||||
+ if (system) {
|
||||
+ ref_field->system = kstrdup(system, GFP_KERNEL);
|
||||
+ if (!ref_field->system)
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ if (event_name) {
|
||||
+ ref_field->event_name = kstrdup(event_name, GFP_KERNEL);
|
||||
+ if (!ref_field->event_name) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ ref_field->name = kstrdup(var_field->var.name, GFP_KERNEL);
|
||||
+ if (!ref_field->name) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+
|
||||
+ ref_field->type = kstrdup(var_field->type, GFP_KERNEL);
|
||||
+ if (!ref_field->type) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto free;
|
||||
+ }
|
||||
+ out:
|
||||
+ return err;
|
||||
+ free:
|
||||
+ kfree(ref_field->system);
|
||||
+ kfree(ref_field->event_name);
|
||||
+ kfree(ref_field->name);
|
||||
+
|
||||
+ goto out;
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *create_var_ref(struct hist_field *var_field,
|
||||
+ char *system, char *event_name)
|
||||
+{
|
||||
+ unsigned long flags = HIST_FIELD_FL_VAR_REF;
|
||||
+ struct hist_field *ref_field;
|
||||
+
|
||||
+ ref_field = create_hist_field(var_field->hist_data, NULL, flags, NULL);
|
||||
+ if (ref_field) {
|
||||
+ if (init_var_ref(ref_field, var_field, system, event_name)) {
|
||||
+ destroy_hist_field(ref_field, 0);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ref_field;
|
||||
+}
|
||||
+
|
||||
+static bool is_var_ref(char *var_name)
|
||||
+{
|
||||
+ if (!var_name || strlen(var_name) < 2 || var_name[0] != '$')
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+static char *field_name_from_var(struct hist_trigger_data *hist_data,
|
||||
+ char *var_name)
|
||||
+{
|
||||
+ char *name, *field;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->attrs->var_defs.n_vars; i++) {
|
||||
+ name = hist_data->attrs->var_defs.name[i];
|
||||
+
|
||||
+ if (strcmp(var_name, name) == 0) {
|
||||
+ field = hist_data->attrs->var_defs.expr[i];
|
||||
+ if (contains_operator(field) || is_var_ref(field))
|
||||
+ continue;
|
||||
+ return field;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static char *local_field_var_ref(struct hist_trigger_data *hist_data,
|
||||
+ char *system, char *event_name,
|
||||
+ char *var_name)
|
||||
+{
|
||||
+ struct trace_event_call *call;
|
||||
+
|
||||
+ if (system && event_name) {
|
||||
+ call = hist_data->event_file->event_call;
|
||||
+
|
||||
+ if (strcmp(system, call->class->system) != 0)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (strcmp(event_name, trace_event_name(call)) != 0)
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (!!system != !!event_name)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (!is_var_ref(var_name))
|
||||
+ return NULL;
|
||||
+
|
||||
+ var_name++;
|
||||
+
|
||||
+ return field_name_from_var(hist_data, var_name);
|
||||
+}
|
||||
+
|
||||
+static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
|
||||
+ char *system, char *event_name,
|
||||
+ char *var_name)
|
||||
+{
|
||||
+ struct hist_field *var_field = NULL, *ref_field = NULL;
|
||||
+
|
||||
+ if (!is_var_ref(var_name))
|
||||
+ return NULL;
|
||||
+
|
||||
+ var_name++;
|
||||
+
|
||||
+ var_field = find_event_var(hist_data, system, event_name, var_name);
|
||||
+ if (var_field)
|
||||
+ ref_field = create_var_ref(var_field, system, event_name);
|
||||
+
|
||||
+ return ref_field;
|
||||
+}
|
||||
+
|
||||
static struct ftrace_event_field *
|
||||
parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
|
||||
char *field_str, unsigned long *flags)
|
||||
@@ -891,10 +1392,40 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
|
||||
struct trace_event_file *file, char *str,
|
||||
unsigned long *flags, char *var_name)
|
||||
{
|
||||
+ char *s, *ref_system = NULL, *ref_event = NULL, *ref_var = str;
|
||||
struct ftrace_event_field *field = NULL;
|
||||
struct hist_field *hist_field = NULL;
|
||||
int ret = 0;
|
||||
|
||||
+ s = strchr(str, '.');
|
||||
+ if (s) {
|
||||
+ s = strchr(++s, '.');
|
||||
+ if (s) {
|
||||
+ ref_system = strsep(&str, ".");
|
||||
+ if (!str) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ ref_event = strsep(&str, ".");
|
||||
+ if (!str) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ ref_var = str;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var);
|
||||
+ if (!s) {
|
||||
+ hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var);
|
||||
+ if (hist_field) {
|
||||
+ hist_data->var_refs[hist_data->n_var_refs] = hist_field;
|
||||
+ hist_field->var_ref_idx = hist_data->n_var_refs++;
|
||||
+ return hist_field;
|
||||
+ }
|
||||
+ } else
|
||||
+ str = s;
|
||||
+
|
||||
field = parse_field(hist_data, file, str, flags);
|
||||
if (IS_ERR(field)) {
|
||||
ret = PTR_ERR(field);
|
||||
@@ -1066,6 +1597,9 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
|
||||
goto free;
|
||||
}
|
||||
|
||||
+ operand1->read_once = true;
|
||||
+ operand2->read_once = true;
|
||||
+
|
||||
expr->operands[0] = operand1;
|
||||
expr->operands[1] = operand2;
|
||||
expr->operator = field_op;
|
||||
@@ -1238,6 +1772,12 @@ static int create_key_field(struct hist_trigger_data *hist_data,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR_REF) {
|
||||
+ destroy_hist_field(hist_field, 0);
|
||||
+ ret = -EINVAL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
key_size = hist_field->size;
|
||||
}
|
||||
|
||||
@@ -1576,6 +2116,7 @@ create_hist_data(unsigned int map_bits,
|
||||
|
||||
hist_data->attrs = attrs;
|
||||
hist_data->remove = remove;
|
||||
+ hist_data->event_file = file;
|
||||
|
||||
ret = create_hist_fields(hist_data, file);
|
||||
if (ret)
|
||||
@@ -1598,12 +2139,6 @@ create_hist_data(unsigned int map_bits,
|
||||
ret = create_tracing_map_fields(hist_data);
|
||||
if (ret)
|
||||
goto free;
|
||||
-
|
||||
- ret = tracing_map_init(hist_data->map);
|
||||
- if (ret)
|
||||
- goto free;
|
||||
-
|
||||
- hist_data->event_file = file;
|
||||
out:
|
||||
return hist_data;
|
||||
free:
|
||||
@@ -1618,12 +2153,17 @@ create_hist_data(unsigned int map_bits,
|
||||
|
||||
static void hist_trigger_elt_update(struct hist_trigger_data *hist_data,
|
||||
struct tracing_map_elt *elt, void *rec,
|
||||
- struct ring_buffer_event *rbe)
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ u64 *var_ref_vals)
|
||||
{
|
||||
+ struct hist_elt_data *elt_data;
|
||||
struct hist_field *hist_field;
|
||||
unsigned int i, var_idx;
|
||||
u64 hist_val;
|
||||
|
||||
+ elt_data = elt->private_data;
|
||||
+ elt_data->var_ref_vals = var_ref_vals;
|
||||
+
|
||||
for_each_hist_val_field(i, hist_data) {
|
||||
hist_field = hist_data->fields[i];
|
||||
hist_val = hist_field->fn(hist_field, elt, rbe, rec);
|
||||
@@ -1675,6 +2215,7 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
struct hist_trigger_data *hist_data = data->private_data;
|
||||
bool use_compound_key = (hist_data->n_keys > 1);
|
||||
unsigned long entries[HIST_STACKTRACE_DEPTH];
|
||||
+ u64 var_ref_vals[TRACING_MAP_VARS_MAX];
|
||||
char compound_key[HIST_KEY_SIZE_MAX];
|
||||
struct tracing_map_elt *elt = NULL;
|
||||
struct stack_trace stacktrace;
|
||||
@@ -1714,9 +2255,15 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
if (use_compound_key)
|
||||
key = compound_key;
|
||||
|
||||
+ if (hist_data->n_var_refs &&
|
||||
+ !resolve_var_refs(hist_data, key, var_ref_vals, false))
|
||||
+ return;
|
||||
+
|
||||
elt = tracing_map_insert(hist_data->map, key);
|
||||
- if (elt)
|
||||
- hist_trigger_elt_update(hist_data, elt, rec, rbe);
|
||||
+ if (!elt)
|
||||
+ return;
|
||||
+
|
||||
+ hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals);
|
||||
}
|
||||
|
||||
static void hist_trigger_stacktrace_print(struct seq_file *m,
|
||||
@@ -1933,8 +2480,11 @@ static void hist_field_print(struct seq_file *m, struct hist_field *hist_field)
|
||||
|
||||
if (hist_field->flags & HIST_FIELD_FL_TIMESTAMP)
|
||||
seq_puts(m, "common_timestamp");
|
||||
- else if (field_name)
|
||||
+ else if (field_name) {
|
||||
+ if (hist_field->flags & HIST_FIELD_FL_VAR_REF)
|
||||
+ seq_putc(m, '$');
|
||||
seq_printf(m, "%s", field_name);
|
||||
+ }
|
||||
|
||||
if (hist_field->flags) {
|
||||
const char *flags_str = get_hist_field_flags(hist_field);
|
||||
@@ -2074,7 +2624,11 @@ static void event_hist_trigger_free(struct event_trigger_ops *ops,
|
||||
if (!data->ref) {
|
||||
if (data->name)
|
||||
del_named_trigger(data);
|
||||
+
|
||||
trigger_data_free(data);
|
||||
+
|
||||
+ remove_hist_vars(hist_data);
|
||||
+
|
||||
destroy_hist_data(hist_data);
|
||||
}
|
||||
}
|
||||
@@ -2287,23 +2841,55 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
goto out;
|
||||
}
|
||||
|
||||
- list_add_rcu(&data->list, &file->triggers);
|
||||
ret++;
|
||||
|
||||
- update_cond_flag(file);
|
||||
-
|
||||
if (hist_data->enable_timestamps)
|
||||
tracing_set_time_stamp_abs(file->tr, true);
|
||||
+ out:
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int hist_trigger_enable(struct event_trigger_data *data,
|
||||
+ struct trace_event_file *file)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ list_add_tail_rcu(&data->list, &file->triggers);
|
||||
+
|
||||
+ update_cond_flag(file);
|
||||
|
||||
if (trace_event_trigger_enable_disable(file, 1) < 0) {
|
||||
list_del_rcu(&data->list);
|
||||
update_cond_flag(file);
|
||||
ret--;
|
||||
}
|
||||
- out:
|
||||
+
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static bool hist_trigger_check_refs(struct event_trigger_data *data,
|
||||
+ struct trace_event_file *file)
|
||||
+{
|
||||
+ struct hist_trigger_data *hist_data = data->private_data;
|
||||
+ struct event_trigger_data *test, *named_data = NULL;
|
||||
+
|
||||
+ if (hist_data->attrs->name)
|
||||
+ named_data = find_named_trigger(hist_data->attrs->name);
|
||||
+
|
||||
+ list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
+ if (!hist_trigger_match(data, test, named_data, false))
|
||||
+ continue;
|
||||
+ hist_data = test->private_data;
|
||||
+ if (check_var_refs(hist_data))
|
||||
+ return true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
struct event_trigger_data *data,
|
||||
struct trace_event_file *file)
|
||||
@@ -2336,11 +2922,30 @@ static void hist_unregister_trigger(char *glob, struct event_trigger_ops *ops,
|
||||
}
|
||||
}
|
||||
|
||||
+static bool hist_file_check_refs(struct trace_event_file *file)
|
||||
+{
|
||||
+ struct hist_trigger_data *hist_data;
|
||||
+ struct event_trigger_data *test;
|
||||
+
|
||||
+ list_for_each_entry_rcu(test, &file->triggers, list) {
|
||||
+ if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
+ hist_data = test->private_data;
|
||||
+ if (check_var_refs(hist_data))
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
static void hist_unreg_all(struct trace_event_file *file)
|
||||
{
|
||||
struct event_trigger_data *test, *n;
|
||||
struct hist_trigger_data *hist_data;
|
||||
|
||||
+ if (hist_file_check_refs(file))
|
||||
+ return;
|
||||
+
|
||||
list_for_each_entry_safe(test, n, &file->triggers, list) {
|
||||
if (test->cmd_ops->trigger_type == ETT_EVENT_HIST) {
|
||||
hist_data = test->private_data;
|
||||
@@ -2416,6 +3021,11 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
+ if (hist_trigger_check_refs(trigger_data, file)) {
|
||||
+ ret = -EBUSY;
|
||||
+ goto out_free;
|
||||
+ }
|
||||
+
|
||||
cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
|
||||
ret = 0;
|
||||
goto out_free;
|
||||
@@ -2433,14 +3043,33 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
goto out_free;
|
||||
} else if (ret < 0)
|
||||
goto out_free;
|
||||
+
|
||||
+ if (get_named_trigger_data(trigger_data))
|
||||
+ goto enable;
|
||||
+
|
||||
+ if (has_hist_vars(hist_data))
|
||||
+ save_hist_vars(hist_data);
|
||||
+
|
||||
+ ret = tracing_map_init(hist_data->map);
|
||||
+ if (ret)
|
||||
+ goto out_unreg;
|
||||
+enable:
|
||||
+ ret = hist_trigger_enable(trigger_data, file);
|
||||
+ if (ret)
|
||||
+ goto out_unreg;
|
||||
+
|
||||
/* Just return zero, not the number of registered triggers */
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
+ out_unreg:
|
||||
+ cmd_ops->unreg(glob+1, trigger_ops, trigger_data, file);
|
||||
out_free:
|
||||
if (cmd_ops->set_filter)
|
||||
cmd_ops->set_filter(NULL, trigger_data, NULL);
|
||||
|
||||
+ remove_hist_vars(hist_data);
|
||||
+
|
||||
kfree(trigger_data);
|
||||
|
||||
destroy_hist_data(hist_data);
|
||||
diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c
|
||||
index 4c269f2e00a4..24d42350d738 100644
|
||||
--- a/kernel/trace/trace_events_trigger.c
|
||||
+++ b/kernel/trace/trace_events_trigger.c
|
||||
@@ -915,6 +915,12 @@ void set_named_trigger_data(struct event_trigger_data *data,
|
||||
data->named_data = named_data;
|
||||
}
|
||||
|
||||
+struct event_trigger_data *
|
||||
+get_named_trigger_data(struct event_trigger_data *data)
|
||||
+{
|
||||
+ return data->named_data;
|
||||
+}
|
||||
+
|
||||
static void
|
||||
traceon_trigger(struct event_trigger_data *data, void *rec,
|
||||
struct ring_buffer_event *event)
|
||||
--
|
||||
2.19.2
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
From bc65875eba8adfc360b79baf7cc2772354f4824a Mon Sep 17 00:00:00 2001
|
||||
From: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Date: Mon, 15 Jan 2018 20:51:57 -0600
|
||||
Subject: [PATCH 100/450] tracing: Add hist trigger action hook
|
||||
|
||||
Add a hook for executing extra actions whenever a histogram entry is
|
||||
added or updated.
|
||||
|
||||
The default 'action' when a hist entry is added to a histogram is to
|
||||
update the set of values associated with it. Some applications may
|
||||
want to perform additional actions at that point, such as generate
|
||||
another event, or compare and save a maximum.
|
||||
|
||||
Add a simple framework for doing that; specific actions will be
|
||||
implemented on top of it in later patches.
|
||||
|
||||
Link: http://lkml.kernel.org/r/9482ba6a3eaf5ca6e60954314beacd0e25c05b24.1516069914.git.tom.zanussi@linux.intel.com
|
||||
|
||||
Signed-off-by: Tom Zanussi <tom.zanussi@linux.intel.com>
|
||||
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
|
||||
(cherry picked from commit b91ae245c2f781e6da0532d8545f51a0f1291cc0)
|
||||
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||||
---
|
||||
kernel/trace/trace_events_hist.c | 106 ++++++++++++++++++++++++++++++-
|
||||
1 file changed, 104 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
|
||||
index 2bc1f76915ef..e5fea3cf60e9 100644
|
||||
--- a/kernel/trace/trace_events_hist.c
|
||||
+++ b/kernel/trace/trace_events_hist.c
|
||||
@@ -33,6 +33,7 @@ typedef u64 (*hist_field_fn_t) (struct hist_field *field,
|
||||
|
||||
#define HIST_FIELD_OPERANDS_MAX 2
|
||||
#define HIST_FIELDS_MAX (TRACING_MAP_FIELDS_MAX + TRACING_MAP_VARS_MAX)
|
||||
+#define HIST_ACTIONS_MAX 8
|
||||
|
||||
enum field_op_id {
|
||||
FIELD_OP_NONE,
|
||||
@@ -242,6 +243,9 @@ struct hist_trigger_attrs {
|
||||
char *assignment_str[TRACING_MAP_VARS_MAX];
|
||||
unsigned int n_assignments;
|
||||
|
||||
+ char *action_str[HIST_ACTIONS_MAX];
|
||||
+ unsigned int n_actions;
|
||||
+
|
||||
struct var_defs var_defs;
|
||||
};
|
||||
|
||||
@@ -261,6 +265,21 @@ struct hist_trigger_data {
|
||||
bool remove;
|
||||
struct hist_field *var_refs[TRACING_MAP_VARS_MAX];
|
||||
unsigned int n_var_refs;
|
||||
+
|
||||
+ struct action_data *actions[HIST_ACTIONS_MAX];
|
||||
+ unsigned int n_actions;
|
||||
+};
|
||||
+
|
||||
+struct action_data;
|
||||
+
|
||||
+typedef void (*action_fn_t) (struct hist_trigger_data *hist_data,
|
||||
+ struct tracing_map_elt *elt, void *rec,
|
||||
+ struct ring_buffer_event *rbe,
|
||||
+ struct action_data *data, u64 *var_ref_vals);
|
||||
+
|
||||
+struct action_data {
|
||||
+ action_fn_t fn;
|
||||
+ unsigned int var_ref_idx;
|
||||
};
|
||||
|
||||
static u64 hist_field_timestamp(struct hist_field *hist_field,
|
||||
@@ -764,6 +783,9 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
|
||||
for (i = 0; i < attrs->n_assignments; i++)
|
||||
kfree(attrs->assignment_str[i]);
|
||||
|
||||
+ for (i = 0; i < attrs->n_actions; i++)
|
||||
+ kfree(attrs->action_str[i]);
|
||||
+
|
||||
kfree(attrs->name);
|
||||
kfree(attrs->sort_key_str);
|
||||
kfree(attrs->keys_str);
|
||||
@@ -771,6 +793,16 @@ static void destroy_hist_trigger_attrs(struct hist_trigger_attrs *attrs)
|
||||
kfree(attrs);
|
||||
}
|
||||
|
||||
+static int parse_action(char *str, struct hist_trigger_attrs *attrs)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (attrs->n_actions >= HIST_ACTIONS_MAX)
|
||||
+ return ret;
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -854,8 +886,9 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
|
||||
else if (strcmp(str, "clear") == 0)
|
||||
attrs->clear = true;
|
||||
else {
|
||||
- ret = -EINVAL;
|
||||
- goto free;
|
||||
+ ret = parse_action(str, attrs);
|
||||
+ if (ret)
|
||||
+ goto free;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2047,11 +2080,55 @@ static int create_sort_keys(struct hist_trigger_data *hist_data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static void destroy_actions(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->n_actions; i++) {
|
||||
+ struct action_data *data = hist_data->actions[i];
|
||||
+
|
||||
+ kfree(data);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int parse_actions(struct hist_trigger_data *hist_data)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+ int ret = 0;
|
||||
+ char *str;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->attrs->n_actions; i++) {
|
||||
+ str = hist_data->attrs->action_str[i];
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int create_actions(struct hist_trigger_data *hist_data,
|
||||
+ struct trace_event_file *file)
|
||||
+{
|
||||
+ struct action_data *data;
|
||||
+ unsigned int i;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->attrs->n_actions; i++) {
|
||||
+ data = hist_data->actions[i];
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static void destroy_hist_data(struct hist_trigger_data *hist_data)
|
||||
{
|
||||
+ if (!hist_data)
|
||||
+ return;
|
||||
+
|
||||
destroy_hist_trigger_attrs(hist_data->attrs);
|
||||
destroy_hist_fields(hist_data);
|
||||
tracing_map_destroy(hist_data->map);
|
||||
+
|
||||
+ destroy_actions(hist_data);
|
||||
+
|
||||
kfree(hist_data);
|
||||
}
|
||||
|
||||
@@ -2118,6 +2195,10 @@ create_hist_data(unsigned int map_bits,
|
||||
hist_data->remove = remove;
|
||||
hist_data->event_file = file;
|
||||
|
||||
+ ret = parse_actions(hist_data);
|
||||
+ if (ret)
|
||||
+ goto free;
|
||||
+
|
||||
ret = create_hist_fields(hist_data, file);
|
||||
if (ret)
|
||||
goto free;
|
||||
@@ -2209,6 +2290,20 @@ static inline void add_to_key(char *compound_key, void *key,
|
||||
memcpy(compound_key + key_field->offset, key, size);
|
||||
}
|
||||
|
||||
+static void
|
||||
+hist_trigger_actions(struct hist_trigger_data *hist_data,
|
||||
+ struct tracing_map_elt *elt, void *rec,
|
||||
+ struct ring_buffer_event *rbe, u64 *var_ref_vals)
|
||||
+{
|
||||
+ struct action_data *data;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ for (i = 0; i < hist_data->n_actions; i++) {
|
||||
+ data = hist_data->actions[i];
|
||||
+ data->fn(hist_data, elt, rec, rbe, data, var_ref_vals);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
struct ring_buffer_event *rbe)
|
||||
{
|
||||
@@ -2264,6 +2359,9 @@ static void event_hist_trigger(struct event_trigger_data *data, void *rec,
|
||||
return;
|
||||
|
||||
hist_trigger_elt_update(hist_data, elt, rec, rbe, var_ref_vals);
|
||||
+
|
||||
+ if (resolve_var_refs(hist_data, key, var_ref_vals, true))
|
||||
+ hist_trigger_actions(hist_data, elt, rec, rbe, var_ref_vals);
|
||||
}
|
||||
|
||||
static void hist_trigger_stacktrace_print(struct seq_file *m,
|
||||
@@ -3050,6 +3148,10 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
|
||||
if (has_hist_vars(hist_data))
|
||||
save_hist_vars(hist_data);
|
||||
|
||||
+ ret = create_actions(hist_data, file);
|
||||
+ if (ret)
|
||||
+ goto out_unreg;
|
||||
+
|
||||
ret = tracing_map_init(hist_data->map);
|
||||
if (ret)
|
||||
goto out_unreg;
|
||||
--
|
||||
2.19.2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user