From 27600805e62f800bacf990354632eae4e487d34c Mon Sep 17 00:00:00 2001 From: Yazen Ghannam Date: Thu, 3 Sep 2026 10:43:25 -0500 Subject: x86/amd_node: Fix PCI device reference counting in amd_smn_init() The local "root" pointer is a temporary variable used during the device search. Therefore, refcount related to the search iterators should be cleaned up after the search is complete. Use the __free() cleanup macro to ensure the refcount is decremented when the temporary pointer goes out of scope. Additionally, increment the refcount when caching a root pointer. This ensures the in-use refcount is separate from the temporary search refcounting. Finally, drop the redundant "root = NULL" before the second search loop. The pci_get_class() iterator always decrements the refcount of its "from" argument, so the first loop can only fall through with "root" already NULL. Fixes: 0a4b61d9c2e4 ("x86/amd_node: Fix AMD root device caching") Closes: https://sashiko.dev/#/patchset/20260806160159.230453-1-jason.andryuk%40amd.com Reported-by: Sashiko Assisted-by: LLM Signed-off-by: Yazen Ghannam Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Mario Limonciello (AMD) Cc: Link: https://patch.msgid.link/20260903154325.74343-1-yazen.ghannam@amd.com --- drivers/pps/kc.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 drivers/pps/kc.c (limited to 'drivers/pps/kc.c') diff --git a/drivers/pps/kc.c b/drivers/pps/kc.c new file mode 100644 index 000000000..4f8fffa7e --- /dev/null +++ b/drivers/pps/kc.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS kernel consumer API + * + * Copyright (C) 2009-2010 Alexander Gordeev + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "kc.h" + +/* + * Global variables + */ + +/* state variables to bind kernel consumer */ +static DEFINE_SPINLOCK(pps_kc_hardpps_lock); +/* PPS API (RFC 2783): current source and mode for kernel consumer */ +static struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */ +static int pps_kc_hardpps_mode; /* mode bits for kernel consumer */ + +/* pps_kc_bind - control PPS kernel consumer binding + * @pps: the PPS source + * @bind_args: kernel consumer bind parameters + * + * This function is used to bind or unbind PPS kernel consumer according to + * supplied parameters. Should not be called in interrupt context. + */ +int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) +{ + /* Check if another consumer is already bound */ + spin_lock_irq(&pps_kc_hardpps_lock); + + /* + * Don't allow PPS_KC_BIND on a removed device. + */ + if (pps->kc_removed) { + spin_unlock_irq(&pps_kc_hardpps_lock); + return -ENODEV; + } + + if (bind_args->edge == 0) + if (pps_kc_hardpps_dev == pps) { + pps_kc_hardpps_mode = 0; + pps_kc_hardpps_dev = NULL; + spin_unlock_irq(&pps_kc_hardpps_lock); + dev_info(&pps->dev, "unbound kernel" + " consumer\n"); + } else { + spin_unlock_irq(&pps_kc_hardpps_lock); + dev_err(&pps->dev, "selected kernel consumer" + " is not bound\n"); + return -EINVAL; + } + else + if (pps_kc_hardpps_dev == NULL || + pps_kc_hardpps_dev == pps) { + pps_kc_hardpps_mode = bind_args->edge; + pps_kc_hardpps_dev = pps; + spin_unlock_irq(&pps_kc_hardpps_lock); + dev_info(&pps->dev, "bound kernel consumer: " + "edge=0x%x\n", bind_args->edge); + } else { + spin_unlock_irq(&pps_kc_hardpps_lock); + dev_err(&pps->dev, "another kernel consumer" + " is already bound\n"); + return -EINVAL; + } + + return 0; +} + +/* pps_kc_remove - unbind kernel consumer on PPS source removal + * @pps: the PPS source + * + * This function is used to disable kernel consumer on PPS source removal + * if this source was bound to PPS kernel consumer. Can be called on any + * source safely. Should not be called in interrupt context. + */ +void pps_kc_remove(struct pps_device *pps) +{ + spin_lock_irq(&pps_kc_hardpps_lock); + + pps->kc_removed = true; + if (pps == pps_kc_hardpps_dev) { + pps_kc_hardpps_mode = 0; + pps_kc_hardpps_dev = NULL; + spin_unlock_irq(&pps_kc_hardpps_lock); + dev_info(&pps->dev, "unbound kernel consumer" + " on device removal\n"); + } else + spin_unlock_irq(&pps_kc_hardpps_lock); +} + +/* pps_kc_event - call hardpps() on PPS event + * @pps: the PPS source + * @ts: PPS event timestamp + * @event: PPS event edge + * + * This function calls hardpps() when an event from bound PPS source occurs. + */ +void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, + int event) +{ + unsigned long flags; + + /* Pass some events to kernel consumer if activated */ + spin_lock_irqsave(&pps_kc_hardpps_lock, flags); + if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) + hardpps(&ts->ts_real, &ts->ts_raw); + spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); +} -- cgit v1.3.1