mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 02:53:36 +02:00
dpll: zl3073x: use __dpll_device_change_ntf() and remove change_work
The change_work was introduced to send device change notifications
from DPLL device callbacks without deadlocking on dpll_lock, since
the callbacks are already invoked under that lock. Now that
__dpll_device_change_ntf() is exported for callers that already
hold dpll_lock, use it directly and remove the change_work
infrastructure entirely.
This eliminates a race condition where change_work could be
re-scheduled after cancel_work_sync() during device teardown,
potentially causing the handler to dereference a freed or NULL
dpll_dev pointer.
Fixes: 9363b48376 ("dpll: zl3073x: Allow to configure phase offset averaging factor")
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
Link: https://patch.msgid.link/20260526074525.1451008-3-ivecera@redhat.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
20040b2a3c
commit
d733f519f6
|
|
@ -1079,15 +1079,6 @@ zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dpll_change_work(struct work_struct *work)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll;
|
||||
|
||||
zldpll = container_of(work, struct zl3073x_dpll, change_work);
|
||||
dpll_device_change_ntf(zldpll->dpll_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 factor,
|
||||
|
|
@ -1113,8 +1104,10 @@ zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
|
|||
* we have to send a notification for other DPLL devices.
|
||||
*/
|
||||
list_for_each_entry(item, &zldpll->dev->dplls, list) {
|
||||
if (item != zldpll)
|
||||
schedule_work(&item->change_work);
|
||||
struct dpll_device *dpll_dev = READ_ONCE(item->dpll_dev);
|
||||
|
||||
if (item != zldpll && dpll_dev)
|
||||
__dpll_device_change_ntf(dpll_dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1627,13 +1620,13 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
|||
static void
|
||||
zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
|
||||
{
|
||||
WARN(!zldpll->dpll_dev, "DPLL device is not registered\n");
|
||||
struct dpll_device *dpll_dev = READ_ONCE(zldpll->dpll_dev);
|
||||
|
||||
cancel_work_sync(&zldpll->change_work);
|
||||
WARN(!dpll_dev, "DPLL device is not registered\n");
|
||||
|
||||
dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll);
|
||||
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
||||
zldpll->dpll_dev = NULL;
|
||||
WRITE_ONCE(zldpll->dpll_dev, NULL);
|
||||
dpll_device_unregister(dpll_dev, &zldpll->ops, zldpll);
|
||||
dpll_device_put(dpll_dev, &zldpll->tracker);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1926,7 +1919,6 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
|
|||
zldpll->dev = zldev;
|
||||
zldpll->id = ch;
|
||||
INIT_LIST_HEAD(&zldpll->pins);
|
||||
INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work);
|
||||
|
||||
return zldpll;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
* @tracker: tracking object for the acquired reference
|
||||
* @lock_status: last saved DPLL lock status
|
||||
* @pins: list of pins
|
||||
* @change_work: device change notification work
|
||||
*/
|
||||
struct zl3073x_dpll {
|
||||
struct list_head list;
|
||||
|
|
@ -35,7 +34,6 @@ struct zl3073x_dpll {
|
|||
dpll_tracker tracker;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct list_head pins;
|
||||
struct work_struct change_work;
|
||||
};
|
||||
|
||||
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user