From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 90C004302F1 for ; Mon, 20 Jul 2026 14:49:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784558968; cv=none; b=lY1jjRsnPGtzyT2wuzh/VH5zDG2WyJuMqcxvppFJyk1NYH+3P2Y+1u4AglGUABv185aF0MC996+cPR2yF0oLGr5vPROU7z8fsrdYIOC7OBTHRtM63fqtIRshOy3hAp6R/0riD73I7xh+pgZDiWptPAy+8eh8dS3ktB4gVO/j33g= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784558968; c=relaxed/simple; bh=xQmgEQZPwodgx8oFRq2Bz7nhC69G6Kf84/fp24uVrX8=; h=Message-ID:Subject:From:To:Cc:Date:In-Reply-To:References: Content-Type:MIME-Version; b=DeBzThudHF4c/JLJ9IpqpDhgIGbvtgkqHZhTXe/dCmXZJZBjeFbPBpG4ddwn00nwBHVzstPgQz+fSv3zDxPu8wDm0JdSe8vhVhje1iGqwy2Z4UKGal6losZNePIiWbMuNKIgrpEijQ3A057hRwH/B+Y3ekaVkOUk/shgaNw6+Lc= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Devc4SgN; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=mDDSBnOY; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Devc4SgN"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="mDDSBnOY" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1784558965; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:autocrypt:autocrypt; bh=lCZf4domhk8HN8iMmiCV6inNWpqnZI8IrFhg4kWyQcw=; b=Devc4SgNnOvn1PHb8811fdn3nbmP6LmM5PNQjVq1aNY40oMpeJxdZGSrKlEbajw9UWS6qK CbI1Z87QmkDwMP8gZrGpi+3kNPezD+C/ygdSKoBNMvnHasl9htpok30dxntp0qt77k7cHU FRH9cI4ZtmMYtp714lNkyzGrcSihEs0= Received: from mail-ed1-f71.google.com (mail-ed1-f71.google.com [209.85.208.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-434-ugfg4DFyOTS8D95J_RuriA-1; Mon, 20 Jul 2026 10:49:24 -0400 X-MC-Unique: ugfg4DFyOTS8D95J_RuriA-1 X-Mimecast-MFC-AGG-ID: ugfg4DFyOTS8D95J_RuriA_1784558963 Received: by mail-ed1-f71.google.com with SMTP id 4fb4d7f45d1cf-69c3d5edf9aso9154104a12.1 for ; Mon, 20 Jul 2026 07:49:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1784558963; x=1785163763; darn=vger.kernel.org; h=mime-version:user-agent:content-transfer-encoding:content-type :autocrypt:references:in-reply-to:date:cc:to:from:subject:message-id :from:to:cc:subject:date:message-id:reply-to:content-type; bh=lCZf4domhk8HN8iMmiCV6inNWpqnZI8IrFhg4kWyQcw=; b=mDDSBnOYQNblvTs0nt3wM7x4qJ5EopM8Bh2Sc5Wv1Lc67TQLgrnEYN/8irRq3lF4Ml P9/tqO+XCafyHx9WvQ/cI6LuAlgsLlHyXyibzI50HJLE0gc+Rq4DNlLJ/zHzON47qv+/ PjFOHYsWLnsJc00ZejFOOkO0F7Ygv8kwxYdrUMnC+Z3RpbelhRfODCbfsBPxStw6q0gf Tqvk78Zw31BRPKPuEraNGXpVwA+0bEdNv1KyaqE6Tn0SJMOhqzdCVHwBUY9CTDZ1c2Aq VTLWOntjM9anyydmCzhOxCyVjtgiZu/SXJX87SSA5zyp3UrS5qMGicZtYrzMITKqgBTK 2eLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784558963; x=1785163763; h=mime-version:user-agent:content-transfer-encoding:content-type :autocrypt:references:in-reply-to:date:cc:to:from:subject:message-id :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=lCZf4domhk8HN8iMmiCV6inNWpqnZI8IrFhg4kWyQcw=; b=i6pC6jCZkERGRgdvRCSpTXDl7J83yWMDTJ2PfrHv/nvU7NmqY7bNv1OdZ602HbLO/V NMYqlVe6pgM5Tj+STstS27KTEzu5pz5q4lsZ5broOmtvMkYWSfwgLV+5O1qwKi3OrYhJ 7e1r3aoLZe5dZ3hn0aZOQtduMyG0bOqOXfgfdgfFjgUEAVDLh++Ij9ulfKYsBgqoqEQA 1Qxk46OTDuIExL+PwT4qRcky8vghfu4QmUZlCk6FNJSXeWpnxgz8qyZUUq8a+1qMNLut KEvapAkLkmrqqZz7wMpJwQ685zihZBZ2EEtd9LWPxOFLZIZqVYwMg25M4xaibjf0sIUa hXqA== X-Forwarded-Encrypted: i=1; AHgh+RrkSDdVSaTTokNZUO09TVA06TGT28+bXL3c+BE+17PJw9mjyinyew5WbhOnsgg+PMXwojRJmAb0K5qUM0c=@vger.kernel.org X-Gm-Message-State: AOJu0YzJi55TMETreAMRprUPDd3Y4O/mutBD2/TtrOaoO9UvN7bSOB0t ha/qDJ1EKYWRD2XfdAnJpKF0z9Qdgrp2vnDDFjV7ljm7Q9SLjgKAYUbaggfC16ynj2gr8NHbh9E qGrVL0UeSGj5kHd2Z4jVZHZl2EybQxJ94v/0G9GJRVyLZW0+lX6YcDkKLp0R6OueKNA== X-Gm-Gg: AfdE7cnS2asRa9cT7yKKguozV8MXE1hj+1m2IHd0jVUk3n9YUR0A0SA94FP478aFWdW j4lR088Oz0WgKRce/fpz0jQoF2pmgx2vF3kmEC601hK/4dC10cgXr+xaUqZhUaIRMJOgDHkHrX0 Z7AE7vqFN7wuDeZUUsNpmbrvX65ZNwOjU3QhAGtKGuhTM6nxTC4PRoFaFVCydpAW34t1Yz9pTZz kFIQqVgOxOKQsJDO3HZCLijE4BuEBksUW49pijUfGzzIulmIi/o6NR1jfQnhjzn4mlY5EXRmGTm 6M1/y+yNqM43h9HdDpLJEzVWuVnoEKUgq0IXU0+71cOfqdxmI2eLLbfP5wwpZoaV0RzY6ViHOHW FMIK2eqzJdDoJ4wK6RsMinQTMKiJSCK42Lco5gtLrSF4JLXp+SP0xboMTf6QBMUfzjGQO4g== X-Received: by 2002:a17:906:7947:b0:c12:9b93:61fd with SMTP id a640c23a62f3a-c16b46aa27amr514319866b.6.1784558962971; Mon, 20 Jul 2026 07:49:22 -0700 (PDT) X-Received: by 2002:a17:906:7947:b0:c12:9b93:61fd with SMTP id a640c23a62f3a-c16b46aa27amr514318366b.6.1784558962460; Mon, 20 Jul 2026 07:49:22 -0700 (PDT) Received: from gmonaco-thinkpadt14gen3.rmtit.csb (212-8-243-115.hosted-by-worldstream.net. [212.8.243.115]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-69e6ffbc8acsm4683223a12.18.2026.07.20.07.49.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 20 Jul 2026 07:49:22 -0700 (PDT) Message-ID: <9fab4d73b21d588d216d57079d34455deb211a88.camel@redhat.com> Subject: Re: [PATCH v4 6/8] rv/tlob: add tlob hybrid automaton monitor From: Gabriele Monaco To: wen.yang@linux.dev Cc: Nam Cao , linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Date: Mon, 20 Jul 2026 16:49:20 +0200 In-Reply-To: <09d656759685edcb0fbeead775300094e7ca5002.1783524627.git.wen.yang@linux.dev> References: <09d656759685edcb0fbeead775300094e7ca5002.1783524627.git.wen.yang@linux.dev> Autocrypt: addr=gmonaco@redhat.com; prefer-encrypt=mutual; keydata=mDMEZuK5YxYJKwYBBAHaRw8BAQdAmJ3dM9Sz6/Hodu33Qrf8QH2bNeNbOikqYtxWFLVm0 1a0JEdhYnJpZWxlIE1vbmFjbyA8Z21vbmFjb0BrZXJuZWwub3JnPoiZBBMWCgBBFiEEysoR+AuB3R Zwp6j270psSVh4TfIFAmjKX2MCGwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgk Q70psSVh4TfIQuAD+JulczTN6l7oJjyroySU55Fbjdvo52xiYYlMjPG7dCTsBAMFI7dSL5zg98I+8 cXY1J7kyNsY6/dcipqBM4RMaxXsOtCRHYWJyaWVsZSBNb25hY28gPGdtb25hY29AcmVkaGF0LmNvb T6InAQTFgoARAIbAwUJBaOagAULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgBYhBMrKEfgLgd0WcK eo9u9KbElYeE3yBQJoymCyAhkBAAoJEO9KbElYeE3yjX4BAJ/ETNnlHn8OjZPT77xGmal9kbT1bC1 7DfrYVISWV2Y1AP9HdAMhWNAvtCtN2S1beYjNybuK6IzWYcFfeOV+OBWRDQ== Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable User-Agent: Evolution 3.60.2 (3.60.2-1.fc44) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 On Wed, 2026-07-08 at 23:38 +0800, wen.yang@linux.dev wrote: > From: Wen Yang > > +/* Uprobe binding list; protected by tlob_uprobe_mutex. */ > +static LIST_HEAD(tlob_uprobe_list); > +static DEFINE_MUTEX(tlob_uprobe_mutex); > + > +/* > + * Serialises duplicate-check + da_handle_start_run_event() per pid. > + * spinlock_t not raw_spinlock_t: uprobe handlers run under Tasks Trace > + * SRCU (rcu_read_lock_trace()), which permits sleeping on PREEMPT_RT. > + */ I don't think these comments add much value. spinlock_t is allowed in RCU critical sections anyway (it's some sort of preemption and RCU is preemptible under PREEMPT_RT). Of course if it isn't really required we don't use a raw spinlock, you don't need a justification here. > +static DEFINE_SPINLOCK(tlob_start_lock); > + > +/* Per-uprobe-binding state: a start + stop probe pair for one binary re= gion. > */ > +struct tlob_uprobe_binding { > + struct list_head list; > + u64 threshold_ns; > + char binpath[TLOB_MAX_PATH]; > + loff_t offset_start; > + loff_t offset_stop; > + DECLARE_RV_UPROBE(start_probe); > + DECLARE_RV_UPROBE(stop_probe); > +}; > + > +/* > + * Per-task teardown invoked by da_monitor_destroy() for each hash entry= . > + * CAS on stopping (0->1) claims exclusive cleanup ownership. I find reading acronyms extremely annoying, since non-locking algorithms are already complex on their own, why don't you just say cmpxchg (which is searchable) instead of CAS. Sure CAS isn't an obscure acronym but I could find at least another 4 different definitions in the kernel tree. > + * > + * No per-entry ha_cancel_timer_sync(): da_monitor_destroy() calls > + * da_monitor_reset_all() + synchronize_rcu() before this hook, and > + * ha_mon_destroying prevents new timer callbacks from running. > + */ > +static inline void tlob_extra_cleanup(struct da_monitor *da_mon) > +{ > + struct ha_monitor *ha_mon =3D to_ha_monitor(da_mon); > + struct tlob_task_state *ws =3D ha_get_target(ha_mon); > + > + if (!ws) > + return; > + > + if (atomic_cmpxchg_release(&ws->stopping, 0, 1) !=3D 0) > + return; > + > + put_task_struct(ws->task); > + /* > + * da_monitor_destroy() has already called synchronize_rcu(); no > + * reader holds ws.=C2=A0 Return the slot directly without call_rcu. > + */ > + llist_add(&ws->free_node, &tlob_ws_free_list); > +} > + > +static inline bool __tlob_acc(struct task_struct *task, ktime_t now, > + =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 enum tlob_acc_idx idx) > +{ > + struct tlob_task_state *ws; > + unsigned long flags; > + > + guard(rcu)(); > + ws =3D da_get_target_by_id(task->pid); > + if (!ws) > + return false; > + raw_spin_lock_irqsave(&ws->entry_lock, flags); > + ws->accs_ns[idx] +=3D ktime_to_ns(ktime_sub(now, ws->last_ts)); > + ws->last_ts =3D now; > + raw_spin_unlock_irqrestore(&ws->entry_lock, flags); > + return true; > +} > + > +/* Accumulate running_ns for prev; returns true if prev is monitored. */ > +static inline bool tlob_acc_running(struct task_struct *task, ktime_t no= w) > +{ > + return __tlob_acc(task, now, TLOB_ACC_RUNNING); > +} > + > +/* Accumulate waiting_ns for next; returns true if next is monitored. */ There's no next and prev here, plus you're describing __tlob_acc()'s behaviour 3 times, I'd say just document that (even if it isn't the primary facing function) and that's all. > +static inline bool tlob_acc_waiting(struct task_struct *task, ktime_t no= w) > +{ > + return __tlob_acc(task, now, TLOB_ACC_WAITING); > +} > + > +/* > + * handle_sched_switch - advance the DA on every context switch. > + * > + * Generates three DA events: > + *=C2=A0=C2=A0 prev, prev_state !=3D 0=C2=A0 -> sleep_tlob=C2=A0=C2=A0= =C2=A0 (running -> sleeping) > + *=C2=A0=C2=A0 prev, prev_state =3D=3D 0=C2=A0 -> preempt_tlob=C2=A0 (ru= nning -> waiting) > + *=C2=A0=C2=A0 next=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 -> switch_in_tlob= (waiting -> running) > + * > + * A single ktime_get() at handler entry is shared by both acc calls so = that > + * prev's running_ns and next's waiting_ns share the same context-switch > + * timestamp; neither absorbs handler overhead into its accumulator. > + * > + * No waiting->sleeping edge exists: a task can only block voluntarily > + * (call schedule()) while it is executing on CPU, which corresponds to > + * the running DA state.=C2=A0 A task in the waiting state is TASK_RUNNI= NG in > + * kernel terms (on the runqueue) and cannot block itself. > + * > + * da_handle_event() is called unconditionally: it skips tasks that have= no > + * monitor entry in the hash table. > + */ > +static void handle_sched_switch(void *data, bool preempt_unused, > + struct task_struct *prev, > + struct task_struct *next, > + unsigned int prev_state) > +{ > + ktime_t now =3D ktime_get(); > + bool prev_preempted =3D (prev_state =3D=3D 0); > + > + if (tlob_acc_running(prev, now)) > + da_handle_event(prev->pid, NULL, > + prev_preempted ? preempt_tlob : sleep_tlob); > + if (tlob_acc_waiting(next, now)) > + da_handle_event(next->pid, NULL, switch_in_tlob); > +} > + > +/* Accumulate sleeping_ns on wakeup; returns true if task is monitored. = */ > +static inline bool tlob_acc_sleeping(struct task_struct *task, ktime_t n= ow) > +{ > + return __tlob_acc(task, now, TLOB_ACC_SLEEPING); > +} > + > +/* > + * handle_sched_wakeup - sleeping -> waiting transition. > + * > + * try_to_wake_up() skips TASK_RUNNING tasks, so this never fires for a > + * task already in running or waiting state. > + */ > +static void handle_sched_wakeup(void *data, struct task_struct *p) > +{ > + ktime_t now =3D ktime_get(); > + > + if (tlob_acc_sleeping(p, now)) > + da_handle_event(p->pid, NULL, wakeup_tlob); > +} > + > +/* > + * handle_sched_process_exit - clean up if a task exits without TRACE_ST= OP. > + * > + * Called in do_exit() context; the task still has a valid pid here. > + * tlob_stop_task() returns -ESRCH if the task is not monitored, which i= s > fine. > + */ > +static void handle_sched_process_exit(void *data, struct task_struct *p, > + =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 bool group_dead) > +{ > + tlob_stop_task(p); > +} > + > +/** > + * tlob_start_task - begin monitoring @task with budget @threshold_ns ns= . > + * @task:=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Task to monito= r; may be current or another task. > + * @threshold_ns: Latency budget in nanoseconds (wall-clock; running + > + *=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0 waiting + sleeping). > + *=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0 Must be in [1000, TLOB_MAX_THRESHOLD_NS]. > + * > + * Returns 0, -ENODEV, -ERANGE, -EALREADY, or -ENOSPC (pool at capacity)= . > + */ > +int tlob_start_task(struct task_struct *task, u64 threshold_ns) > +{ > + struct tlob_task_state *ws; > + > + if (!da_monitor_enabled()) > + return -ENODEV; > + > + if (threshold_ns < TLOB_MIN_THRESHOLD_NS || > + =C2=A0=C2=A0=C2=A0 threshold_ns > TLOB_MAX_THRESHOLD_NS) > + return -ERANGE; > + > + /* Serialise duplicate-check + pool-slot claim; see tlob_start_lock. > */ > + guard(spinlock)(&tlob_start_lock); > + > + /* > + * __da_get_mon_storage() uses hash_for_each_possible_rcu(), which > + * requires an RCU read-side critical section.=C2=A0 On PREEMPT_RT, > + * spinlock_t is an rt_mutex and does not satisfy this requirement. > + */ Also this is probably misleading, getting the monitor requires RCU, then the fact some in some configuration something other than read_lock_rcu() would work too (e.g. disabling preemption) is irrelevant. > + scoped_guard(rcu) { > + if (da_get_target_by_id(task->pid)) > + return -EALREADY; > + } > + > + /* > + * Both tlob_ws_alloc() and da_handle_start_run_event() pop from > + * pre-allocated pools of size TLOB_MAX_MONITORED; NULL return means > + * the pool is at capacity. > + */ > + ws =3D tlob_ws_alloc(); > + if (!ws) > + return -ENOSPC; > + > + ws->task =3D task; > + get_task_struct(task); > + ws->threshold_ns =3D threshold_ns; > + ws->last_ts =3D ktime_get(); > + raw_spin_lock_init(&ws->entry_lock); > + > + /* > + * da_handle_start_run_event() claims a pool slot via > da_prepare_storage(), > + * initialises the monitor, and delivers start_tlob in one step: the > + * generated ha_setup_invariants() resets clk_elapsed and arms the > timer. > + * Returns 0 if the da_monitor_storage pool is exhausted. > + */ And try not to be too specific about the internal implementation of the library, that can change without notice, we don't want to have to update all comments. Here it is indeed non-trivial to assume no space if da_handle_start_run_event() fails, but that's only consequence of the fact that this ws is certainly new (by construction) and the monitor is enabled: da_handle_start_run_event() can only fail if allocation failed. da_handle_start_* functions return 1 if an event was handled, this is by the way not documented.. You could simply say something like: da_handle_start_run_event() returns false if no event was handled, in this case it can happen only if memory allocation failed > + if (!da_handle_start_run_event(task->pid, ws, start_tlob)) { > + put_task_struct(task); > + tlob_ws_direct_return(ws); > + return -ENOSPC; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(tlob_start_task); > + > +/** > + * tlob_stop_task - stop monitoring @task. > + * @task: Task to stop. > + * > + * CAS on ws->stopping (0->1) under RCU claims cleanup ownership; > + * the winner cancels the timer synchronously and frees all resources. > + * > + * Returns 0, -EOVERFLOW (budget exceeded), -ESRCH (not monitored), > + * or -EAGAIN (concurrent caller claimed cleanup). > + */ > +int tlob_stop_task(struct task_struct *task) > +{ > + struct da_monitor *da_mon; > + struct ha_monitor *ha_mon; > + struct tlob_task_state *ws; > + bool budget_exceeded; > + > + scoped_guard(rcu) { > + ws =3D da_get_target_by_id(task->pid); > + if (!ws) > + return -ESRCH; > + > + da_mon =3D da_get_monitor(task->pid, NULL); > + if (unlikely(WARN_ON_ONCE(!da_mon))) > + return -ESRCH; > + > + ha_mon =3D to_ha_monitor(da_mon); > + > + /* > + * CAS (0->1) claims cleanup ownership under RCU (ws > guaranteed valid). > + * _release pairs with atomic_read_acquire in > ha_setup_invariants. > + */ > + if (atomic_cmpxchg_release(&ws->stopping, 0, 1) !=3D 0) > + return -EAGAIN; > + } > + /* > + * ws and ha_mon are used below outside the RCU guard.=C2=A0 This is sa= fe: > + * the winning CAS (stopping: 0->1) is the only path that frees ws, > + * and da_destroy_storage() below is the only call that returns the > + * pool slot.=C2=A0 No concurrent path can free either object. > + */ > + > + /* Wait for in-flight timer callback before reading da_monitoring. */ > + ha_cancel_timer_sync(ha_mon); > + > + /* Timer fired first -> budget exceeded; otherwise reset normally. */ > + scoped_guard(rcu) { > + budget_exceeded =3D !da_monitoring(da_mon); > + if (!budget_exceeded) > + da_monitor_reset(da_mon); > + } > + da_destroy_storage(task->pid); Here you're playing with the state machine, that looks fragile from a monitor code. You could probably have budget_exceeded as part of your target and set it from tlob_reset_notify() when you know there was an expiration. Perhaps "stop" could be yet another event in the state machine, bringing to the "stopped" state, which would automatically call reset(), you only need to grab ws->stopping before handling the event for tlob_reset_notify() to work as expected. Though you'd probably still need ha_cancel_timer_sync() for the rare but not impossible in-flight timer not yet in RCU critical section.. I'd rather avoid as much as possible bringing in ha_mon/da_mon, but you probably cannot do better here, just you could avoid relying on da_monitoring(). What do you think? Thanks, Gabriele