summaryrefslogtreecommitdiffstats
path: root/drivers/media/rc/ir-jvc-decoder.c
diff options
context:
space:
mode:
authorTim Chen <tim.c.chen@linux.intel.com>2026-09-21 17:37:22 -0700
committerIngo Molnar <mingo@kernel.org>2026-09-22 10:49:53 +0200
commit0d6526f82c3cdefcca47f73f5fc08dc6f335eac6 (patch)
tree176dc2d685debcfb082c151ee94f2425ac96b364 /drivers/media/rc/ir-jvc-decoder.c
downloadlinux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.tar.gz
linux-stable-0d6526f82c3cdefcca47f73f5fc08dc6f335eac6.zip
sched/cache: Keep nr_pref_llc_running in the runnable domain, to fix LLC mis-scheduling buggrafted
alb_break_llc() decides whether to break LLC preference during active load balance. It does so by testing that every runnable fair task on the source rq prefers its LLC: env->src_rq->nr_pref_llc_running == env->src_rq->cfs.h_nr_runnable But the two counters cover different sets. nr_pref_llc_running is updated in account_llc_enqueue()/account_llc_dequeue(), next to cfs_rq->nr_queued, so it follows queued tasks. h_nr_runnable is updated in set_delayed()/ clear_delayed() and drops delay-dequeued tasks. So under DELAY_DEQUEUE, a preferring task that goes to sleep stays counted in nr_pref_llc_running while h_nr_runnable falls. The equality then breaks, alb_break_llc() returns false, and active balance is free to pull a task off its preferred LLC. Active balance only moves runnable tasks, and this is the only LLC check it consults: once the stopper runs, LBF_ACTIVE_LB skips the per-task test in can_migrate_task(). The runnable set is the one we want. Fix it on the counter side. A task should be counted in nr_pref_llc_running exactly while it is both queued on its preferred LLC (pref_llc_queued) and runnable (!sched_delayed). Define that membership once in task_pref_llc_runnable(), and adjust the counter only through pref_llc_running_inc()/pref_llc_running_dec() from the four sites that change either input: account_llc_enqueue(), account_llc_dequeue(), set_delayed() and clear_delayed(). Gating every update on the same predicate keeps the delay, wake and dequeue paths from double-counting or underflowing; see the comments at those sites for the ordering. nr_llc_running and sd->llc_counts are not touched and stay on queued semantics. Fixes: 714059f79ff0 ("sched/cache: Handle moving single tasks to/from their preferred LLC") Closes: https://lore.kernel.org/lkml/20260827135000.735138-1-zhanxusheng@xiaomi.com/ Reported-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Suggested-by: Chen Yu <yu.c.chen@intel.com> Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Kayra Cizmeci <kayracizmeci@gmail.com> Cc: <stable@kernel.org> # v7.2.x Link: https://patch.msgid.link/06af61afedac32e6477f57feb4d658f6c411c3af.1790035273.git.tim.c.chen@linux.intel.com
Diffstat (limited to 'drivers/media/rc/ir-jvc-decoder.c')
-rw-r--r--drivers/media/rc/ir-jvc-decoder.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
new file mode 100644
index 000000000..8b10954d2
--- /dev/null
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* ir-jvc-decoder.c - handle JVC IR Pulse/Space protocol
+ *
+ * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define JVC_NBITS 16 /* dev(8) + func(8) */
+#define JVC_UNIT 525 /* us */
+#define JVC_HEADER_PULSE (16 * JVC_UNIT) /* lack of header -> repeat */
+#define JVC_HEADER_SPACE (8 * JVC_UNIT)
+#define JVC_BIT_PULSE (1 * JVC_UNIT)
+#define JVC_BIT_0_SPACE (1 * JVC_UNIT)
+#define JVC_BIT_1_SPACE (3 * JVC_UNIT)
+#define JVC_TRAILER_PULSE (1 * JVC_UNIT)
+#define JVC_TRAILER_SPACE (35 * JVC_UNIT)
+
+enum jvc_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+ STATE_BIT_PULSE,
+ STATE_BIT_SPACE,
+ STATE_TRAILER_PULSE,
+ STATE_TRAILER_SPACE,
+ STATE_CHECK_REPEAT,
+};
+
+/**
+ * ir_jvc_decode() - Decode one JVC pulse or space
+ * @dev: the struct rc_dev descriptor of the device
+ * @ev: the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+ struct jvc_dec *data = &dev->raw->jvc;
+
+ if (!is_timing_event(ev)) {
+ if (ev.overflow)
+ data->state = STATE_INACTIVE;
+ return 0;
+ }
+
+ if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2))
+ goto out;
+
+ dev_dbg(&dev->dev, "JVC decode started at state %d (%uus %s)\n",
+ data->state, ev.duration, TO_STR(ev.pulse));
+
+again:
+ switch (data->state) {
+
+ case STATE_INACTIVE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+ break;
+
+ data->count = 0;
+ data->first = true;
+ data->toggle = !data->toggle;
+ data->state = STATE_HEADER_SPACE;
+ return 0;
+
+ case STATE_HEADER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, JVC_HEADER_SPACE, JVC_UNIT / 2))
+ break;
+
+ data->state = STATE_BIT_PULSE;
+ return 0;
+
+ case STATE_BIT_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, JVC_BIT_PULSE, JVC_UNIT / 2))
+ break;
+
+ data->state = STATE_BIT_SPACE;
+ return 0;
+
+ case STATE_BIT_SPACE:
+ if (ev.pulse)
+ break;
+
+ data->bits <<= 1;
+ if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) {
+ data->bits |= 1;
+ decrease_duration(&ev, JVC_BIT_1_SPACE);
+ } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2))
+ decrease_duration(&ev, JVC_BIT_0_SPACE);
+ else
+ break;
+ data->count++;
+
+ if (data->count == JVC_NBITS)
+ data->state = STATE_TRAILER_PULSE;
+ else
+ data->state = STATE_BIT_PULSE;
+ return 0;
+
+ case STATE_TRAILER_PULSE:
+ if (!ev.pulse)
+ break;
+
+ if (!eq_margin(ev.duration, JVC_TRAILER_PULSE, JVC_UNIT / 2))
+ break;
+
+ data->state = STATE_TRAILER_SPACE;
+ return 0;
+
+ case STATE_TRAILER_SPACE:
+ if (ev.pulse)
+ break;
+
+ if (!geq_margin(ev.duration, JVC_TRAILER_SPACE, JVC_UNIT / 2))
+ break;
+
+ if (data->first) {
+ u32 scancode;
+ scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) |
+ (bitrev8((data->bits >> 0) & 0xff) << 0);
+ dev_dbg(&dev->dev, "JVC scancode 0x%04x\n", scancode);
+ rc_keydown(dev, RC_PROTO_JVC, scancode, data->toggle);
+ data->first = false;
+ data->old_bits = data->bits;
+ } else if (data->bits == data->old_bits) {
+ dev_dbg(&dev->dev, "JVC repeat\n");
+ rc_repeat(dev);
+ } else {
+ dev_dbg(&dev->dev, "JVC invalid repeat msg\n");
+ break;
+ }
+
+ data->count = 0;
+ data->state = STATE_CHECK_REPEAT;
+ return 0;
+
+ case STATE_CHECK_REPEAT:
+ if (!ev.pulse)
+ break;
+
+ if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+ data->state = STATE_INACTIVE;
+ else
+ data->state = STATE_BIT_PULSE;
+ goto again;
+ }
+
+out:
+ dev_dbg(&dev->dev, "JVC decode failed at state %d (%uus %s)\n",
+ data->state, ev.duration, TO_STR(ev.pulse));
+ data->state = STATE_INACTIVE;
+ return -EINVAL;
+}
+
+static const struct ir_raw_timings_pd ir_jvc_timings = {
+ .header_pulse = JVC_HEADER_PULSE,
+ .header_space = JVC_HEADER_SPACE,
+ .bit_pulse = JVC_BIT_PULSE,
+ .bit_space[0] = JVC_BIT_0_SPACE,
+ .bit_space[1] = JVC_BIT_1_SPACE,
+ .trailer_pulse = JVC_TRAILER_PULSE,
+ .trailer_space = JVC_TRAILER_SPACE,
+ .msb_first = 1,
+};
+
+/**
+ * ir_jvc_encode() - Encode a scancode as a stream of raw events
+ *
+ * @protocol: protocol to encode
+ * @scancode: scancode to encode
+ * @events: array of raw ir events to write into
+ * @max: maximum size of @events
+ *
+ * Returns: The number of events written.
+ * -ENOBUFS if there isn't enough space in the array to fit the
+ * encoding. In this case all @max events will have been written.
+ */
+static int ir_jvc_encode(enum rc_proto protocol, u32 scancode,
+ struct ir_raw_event *events, unsigned int max)
+{
+ struct ir_raw_event *e = events;
+ int ret;
+ u32 raw = (bitrev8((scancode >> 8) & 0xff) << 8) |
+ (bitrev8((scancode >> 0) & 0xff) << 0);
+
+ ret = ir_raw_gen_pd(&e, max, &ir_jvc_timings, JVC_NBITS, raw);
+ if (ret < 0)
+ return ret;
+
+ return e - events;
+}
+
+static struct ir_raw_handler jvc_handler = {
+ .protocols = RC_PROTO_BIT_JVC,
+ .decode = ir_jvc_decode,
+ .encode = ir_jvc_encode,
+ .carrier = 38000,
+ .min_timeout = JVC_TRAILER_SPACE,
+};
+
+static int __init ir_jvc_decode_init(void)
+{
+ ir_raw_handler_register(&jvc_handler);
+
+ printk(KERN_INFO "IR JVC protocol handler initialized\n");
+ return 0;
+}
+
+static void __exit ir_jvc_decode_exit(void)
+{
+ ir_raw_handler_unregister(&jvc_handler);
+}
+
+module_init(ir_jvc_decode_init);
+module_exit(ir_jvc_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_DESCRIPTION("JVC IR protocol decoder");