From nobody Sun Feb 8 20:00:11 2026 Received: from mail-ed1-f74.google.com (mail-ed1-f74.google.com [209.85.208.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E553328606 for ; Wed, 17 Dec 2025 11:21:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765970511; cv=none; b=D0NlcLZjid21yI9f3W+tq6A+D2an6Zr/8/qWdAjn2w+KouWAU0ClB8KUtzh9f6pXkJmRmrVaFsW67Ci3uLovPTgdHUZaefohRool8coX0mWPhmygLgnYdtBAkUqR4mrZ4RitC+xJgSwEj9OttfMfLTrF5Noc+BkAppnscN4s7Sw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765970511; c=relaxed/simple; bh=djGK9a/PbIEvwH7fMw191k8tLn5bmy64ldWAKNGBEIg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=jnCNnelhWjLTHuZA4OCFZHHNAXbPwSr/lds5u2qtWrXB/8EA88x7ynR+os+nz6uDeAfyyZlZhYOaKZk34hFlSlRumd2ognDduELQOFGG81hUtQqAWfQCZg/eURcs7WhcB2/NPC5bw3HXv5yu9Wky2OtprDghZY+BEqAMclOIatA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--lrizzo.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=JLjZaawv; arc=none smtp.client-ip=209.85.208.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--lrizzo.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="JLjZaawv" Received: by mail-ed1-f74.google.com with SMTP id 4fb4d7f45d1cf-64b3eaf9bc4so987100a12.1 for ; Wed, 17 Dec 2025 03:21:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1765970508; x=1766575308; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=XtTq+yFtULAr3S4+lmRLp6BZLceWbGit2YFFxC7qk9w=; b=JLjZaawvzfShJkQdD2BxKLnkn9DruQo8Bq7b7MEYPWAxoq1UytLibWndspyD7kpy0t TVoUbbhfeyi+HnJCBk9MTVyoRJWfbkmLlylF//yxvx2pW8Pa5JLCpznKALWqwD8NT5j9 qsVFn7Kjd+wOwu+SVpLFqNGjnzEDS4VpzFdSsvGb9KDmnfYK/ck+ybXH5ABbs8v6Cj9x 3e63d7C1K60d5h8q5Kv1ZB7uChCTPkcyEeBmbVvM1VMCvUtCs5BfKjXOahMxSFRONbZA 45MPHcNxpaiTy9Vt1zlOlvxbD/xKe13ThZVQF8Z5Ji7P5AhBtBKESbINQQ5R/To2vRLg dEKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1765970508; x=1766575308; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=XtTq+yFtULAr3S4+lmRLp6BZLceWbGit2YFFxC7qk9w=; b=caVEeTELgRU+qSLnkS6j05MTJVlejmZeuDor/GZUBeU2Hie+a/BMpOOlndCQDx6Rad ttPs8+2zDPp6f/PxLKfMlbYr80TFo6HSuxBMXMet3KNNb9Ia5m+Rv+XL8r/6TEcW6pkE EUchR8bxYWez2rZsaPF8+gatJSGvldqZh8/qbAChnDjrdFlcXr1CKFk3b0vW5FS1kx9i uF7ZJ3zbvLUwEwAv1p+La9K7WRAYHUBOGm+hJ57h5cL/YgLRfCbuKgI4Sqy1WWqr89pB s4r1CUCo0sY+BBFWc5YWxUhWq2s/9hDwnWBioNJjqsN2XjETtMW0fgJ8CS4bKTr9rdLz QSkw== X-Gm-Message-State: AOJu0Yz/VGbFrsVGJIKP/ThOcy76F5P+nQ7zWZR9eQ32WMALlJpp9hbi 3Vnq/CrHkY1mEyih0wZhbuU5LF/OQiMtxqdbSwCJdr7eK6GSM1XVS4ATIWlPDd9nrQf2O+5v9Cb MzgxgKQ== X-Google-Smtp-Source: AGHT+IHf617U3EOpdp52V/RhXFK5v3RSOZhwgvcp0C9fXsJFLpQ1TQTfSiq2gZ9LKhWsDSYcQTVE98fnY+g= X-Received: from edsx15.prod.google.com ([2002:aa7:dacf:0:b0:649:93b9:195f]) (user=lrizzo job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6402:2792:b0:649:a84c:745b with SMTP id 4fb4d7f45d1cf-649a84c7534mr15485990a12.3.1765970507467; Wed, 17 Dec 2025 03:21:47 -0800 (PST) Date: Wed, 17 Dec 2025 11:21:28 +0000 In-Reply-To: <20251217112128.1401896-1-lrizzo@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20251217112128.1401896-1-lrizzo@google.com> X-Mailer: git-send-email 2.52.0.305.g3fc767764a-goog Message-ID: <20251217112128.1401896-4-lrizzo@google.com> Subject: [PATCH-v3 3/3] genirq: Configurable default mode for GSIM From: Luigi Rizzo To: Thomas Gleixner , Marc Zyngier , Luigi Rizzo , Paolo Abeni , Andrew Morton , Sean Christopherson , Jacob Pan Cc: linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org, Bjorn Helgaas , Willem de Bruijn , Luigi Rizzo Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" GSIM (Global Software Interrupt Moderation) can be enabled only after the interrupt is created by writing to /proc/irq/NN/soft_moderation. This is impractical when devices that are dynamically created or reconfigured. Add a module parameter irq_moderation.enable_list to specify whether moderation should be enabled at interrupt creation time. This is done with a comma-separated list of patterns (enable_list) matched against interrupt or handler names when the interrupt is created. This allows very flexible control without having to modify every single driver. As an example, one can limit to specific drivers by specifying the handler functions (using parentheses) as below irq_moderation.enable_list=3D"nvme_irq(),vfio_msihandler()" ora apply it to certain interrupt names irq_moderation.enable_list=3D"eth*,vfio*" Change-Id: Id5f7aba5b21ad478e4fb7edd0f00eeb2b83e07d2 Signed-off-by: Luigi Rizzo --- include/linux/irqdesc.h | 5 ++ kernel/irq/irq_moderation.c | 125 ++++++++++++++++++++++++++++++++++++ kernel/irq/manage.c | 4 ++ 3 files changed, 134 insertions(+) diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index d42ce57ff5406..e0785c709d013 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -16,6 +16,7 @@ struct module; struct irq_desc; struct irq_domain; struct pt_regs; +struct irqaction; =20 #ifdef CONFIG_IRQ_SOFT_MODERATION =20 @@ -30,11 +31,15 @@ struct irq_desc_mod { }; =20 void irq_moderation_init_fields(struct irq_desc_mod *mod); +bool irq_moderation_get_default(const struct irqaction *action); +void irq_moderation_enable(struct irq_desc *desc); =20 #else =20 struct irq_desc_mod {}; static inline void irq_moderation_init_fields(struct irq_desc_mod *mod) {} +static inline bool irq_moderation_get_default(const struct irqaction *acti= on) { return false; } +static inline void irq_moderation_enable(struct irq_desc *desc) {} =20 #endif =20 diff --git a/kernel/irq/irq_moderation.c b/kernel/irq/irq_moderation.c index be7d10404a24a..216bd1f83d23e 100644 --- a/kernel/irq/irq_moderation.c +++ b/kernel/irq/irq_moderation.c @@ -50,6 +50,11 @@ * update_ms (default 5, range 1-100) * How often the load is measured and moderation delay updated. * + * enable_list (comma-separated list of handlers or interrupt names) + * Enable moderation at creation for interrupts whose name or handler + * functions match patterns in this list. Example: + * "nvme_irq(),eth*,vfio_msihandler()" + * * Moderation can be enabled/disabled dynamically for individual interrupt= s with * * echo 1 > /proc/irq/NN/soft_moderation # use 0 to disable @@ -142,6 +147,90 @@ void irq_moderation_init_fields(struct irq_desc_mod *m= od) mod->enable =3D false; } =20 +/* + * irq_moderation.enable_list is a comma-separated list of patterns to mat= ch + * against the name eg "eth*" or handler e.g "nvme_irq()" of the interrupt. + * There is one active buffer and one used for updates, so replacement just + * needs to swap the pointer. + */ +#define MAX_CONFIG_LEN 4096 +static struct enable_list { + char buf[MAX_CONFIG_LEN]; + char buf2[MAX_CONFIG_LEN]; + char *cur; + uint len; + struct rw_semaphore rwsem; +} patterns =3D { .cur =3D patterns.buf, .rwsem =3D __RWSEM_INITIALIZER(pat= terns.rwsem) }; + +static int set_enable_list(const char *val, const struct kernel_param *kp) +{ + uint i, len =3D strlen(val); + char *dst; + + if (len >=3D MAX_CONFIG_LEN - 1) + return -E2BIG; + if (val[len - 1] =3D=3D '\n') + len--; + + /* Serialized by procfs. */ + dst =3D patterns.cur =3D=3D patterns.buf ? patterns.buf2 : patterns.buf; + /* Copy and split the string on commas. */ + for (i =3D 0; i < len; i++) + dst[i] =3D val[i] =3D=3D ',' ? '\0' : val[i]; + dst[i] =3D '\0'; + guard(rwsem_write)(&patterns.rwsem); + patterns.cur =3D dst; + patterns.len =3D len; + return 0; +} + +static int get_enable_list(char *buf, const struct kernel_param *kp) +{ + int i; + + /* Join the patterns with commas. */ + guard(rwsem_read)(&patterns.rwsem); + for (i =3D 0; i < patterns.len; i++) + buf[i] =3D patterns.cur[i] =3D=3D '\0' ? ',' : patterns.cur[i]; + buf[i] =3D '\0'; + return patterns.len; +} + +static const struct kernel_param_ops enable_list_ops =3D { + .set =3D set_enable_list, + .get =3D get_enable_list, +}; + +module_param_cb(enable_list, &enable_list_ops, NULL, 0444); + +/* Called early in __setup_irq() without desc->lock. */ +bool irq_moderation_get_default(const struct irqaction *act) +{ + guard(rwsem_read)(&patterns.rwsem); + for (int arg =3D 0; arg < 3; arg++) { + /* buf includes room for "()". */ + char buf[KSYM_SYMBOL_LEN + 3]; + const char *name; + + if (arg =3D=3D 0) { + name =3D act->name; + if (!name) + continue; + } else { + irq_handler_t fn =3D arg =3D=3D 1 ? act->handler : act->thread_fn; + + if (lookup_symbol_name((ulong)fn, buf)) + continue; + memcpy(buf + strlen(buf), "()", 3); + name =3D buf; + } + for (int i =3D 0; i < patterns.len; i +=3D 1 + strlen(patterns.cur + i)) + if (glob_match(patterns.cur + i, name)) + return true; + } + return false; +} + static int set_mode(struct irq_desc *desc, bool enable) { lockdep_assert_held(&desc->lock); @@ -163,6 +252,12 @@ static int set_mode(struct irq_desc *desc, bool enable) return 0; } =20 +/* Called with desc->lock held in __setup_irq(). */ +void irq_moderation_enable(struct irq_desc *desc) +{ + set_mode(desc, true); +} + #pragma clang diagnostic error "-Wformat" /* Print statistics */ static int moderation_show(struct seq_file *p, void *v) @@ -171,6 +266,7 @@ static int moderation_show(struct seq_file *p, void *v) uint delay_us =3D irq_mod_info.delay_us; u64 now =3D ktime_get_ns(); int j, active_cpus =3D 0; + char *buf; =20 #define HEAD_FMT "%5s %8s %8s %4s %8s %11s %11s %11s %11s %11s %= 9s\n" #define BODY_FMT "%5u %8u %8u %4u %8u %11u %11u %11u %11u %11u %= 9u\n" @@ -210,17 +306,24 @@ static int moderation_show(struct seq_file *p, void *= v) ms->stray_irq); } =20 + buf =3D kmalloc(MAX_CONFIG_LEN, GFP_KERNEL); + if (buf) + get_enable_list(buf, NULL); seq_printf(p, "\n" "enabled %s\n" + "enable_list '%s'\n" "delay_us %u\n" "target_intr_rate %u\n" "hardirq_percent %u\n" "update_ms %u\n" "scale_cpus %u\n", str_yes_no(delay_us > 0), + buf ? : "", delay_us, irq_mod_info.target_intr_rate, irq_mod_info.hardirq_percent, irq_mod_info.update_ms, irq_mod_info.scale_cpus); + if (buf) + kfree(buf); =20 seq_printf(p, "intr_rate %lu\n" @@ -258,6 +361,8 @@ static struct param_names param_names[] =3D { /* Entries with no name cannot be set at runtime. */ { "", &irq_mod_info.increase_factor, MIN_SCALING_FACTOR, 128 }, { "", &irq_mod_info.scale_cpus, 50, 1000 }, + /* val =3D NULL indicates a special entry for enable_list. */ + { "enable_list", NULL }, }; =20 static void update_enable_key(void) @@ -270,6 +375,24 @@ static void update_enable_key(void) static_branch_disable(key); } =20 +static ssize_t set_enable_list_from_proc(const char __user *buf, int count= , int ofs) +{ + char *cmd; + + if (count >=3D MAX_CONFIG_LEN) + return -EINVAL; + cmd =3D kmalloc(count + 1, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + if (copy_from_user(cmd, buf, count)) { + kfree(cmd); + return -EFAULT; + } + set_enable_list(cmd + ofs, NULL); + kfree(cmd); + return count; +} + static ssize_t moderation_write(struct file *f, const char __user *buf, si= ze_t count, loff_t *ppos) { uint i, val, copy_len, name_len; @@ -287,6 +410,8 @@ static ssize_t moderation_write(struct file *f, const c= har __user *buf, size_t c if (name_len < 1 || copy_len < name_len + 2 || strncmp(cmd, n->name, nam= e_len) || cmd[name_len] !=3D '=3D') continue; + if (n->val =3D=3D NULL) + return set_enable_list_from_proc(buf, count, name_len + 1); if (kstrtouint(cmd + name_len + 1, 0, &val)) return -EINVAL; WRITE_ONCE(*(n->val), clamp(val, n->min, n->max)); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 8b1b4c8a4f54c..41b6e6ec2b09e 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1449,6 +1449,7 @@ static bool valid_percpu_irqaction(struct irqaction *= old, struct irqaction *new) static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) { + bool use_moderation =3D irq_moderation_get_default(new); struct irqaction *old, **old_ptr; unsigned long flags, thread_mask =3D 0; int ret, nested, shared =3D 0; @@ -1761,6 +1762,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, = struct irqaction *new) =20 irq_pm_install_action(desc, new); =20 + if (use_moderation) + irq_moderation_enable(desc); + /* Reset broken irq detection when installing new handler */ desc->irq_count =3D 0; desc->irqs_unhandled =3D 0; --=20 2.52.0.305.g3fc767764a-goog