Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dpll: fix dpll_pin_on_pin_register() for multiple parent pins

In scenario where pin is registered with multiple parent pins via
dpll_pin_on_pin_register(..), all belonging to the same dpll device.
A second call to dpll_pin_on_pin_unregister(..) would cause a call trace,
as it tries to use already released registration resources (due to fix
introduced in b446631f355e). In this scenario pin was registered twice,
so resources are not yet expected to be release until each registered
pin/pin pair is unregistered.

Currently, the following crash/call trace is produced when ice driver is
removed on the system with installed E810T NIC which includes dpll device:

WARNING: CPU: 51 PID: 9155 at drivers/dpll/dpll_core.c:809 dpll_pin_ops+0x20/0x30
RIP: 0010:dpll_pin_ops+0x20/0x30
Call Trace:
? __warn+0x7f/0x130
? dpll_pin_ops+0x20/0x30
dpll_msg_add_pin_freq+0x37/0x1d0
dpll_cmd_pin_get_one+0x1c0/0x400
? __nlmsg_put+0x63/0x80
dpll_pin_event_send+0x93/0x140
dpll_pin_on_pin_unregister+0x3f/0x100
ice_dpll_deinit_pins+0xa1/0x230 [ice]
ice_remove+0xf1/0x210 [ice]

Fix by adding a parent pointer as a cookie when creating a registration,
also when searching for it. For the regular pins pass NULL, this allows to
create separated registration for each parent the pin is registered with.

Fixes: b446631f355e ("dpll: fix dpll_xa_ref_*_del() for multiple registrations")
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Link: https://lore.kernel.org/r/20240424101636.1491424-1-arkadiusz.kubalewski@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Arkadiusz Kubalewski and committed by
Jakub Kicinski
38d7b94e 0c81ea5a

+33 -25
+33 -25
drivers/dpll/dpll_core.c
··· 42 42 struct list_head list; 43 43 const struct dpll_pin_ops *ops; 44 44 void *priv; 45 + void *cookie; 45 46 }; 46 47 47 48 struct dpll_device *dpll_device_get_by_id(int id) ··· 55 54 56 55 static struct dpll_pin_registration * 57 56 dpll_pin_registration_find(struct dpll_pin_ref *ref, 58 - const struct dpll_pin_ops *ops, void *priv) 57 + const struct dpll_pin_ops *ops, void *priv, 58 + void *cookie) 59 59 { 60 60 struct dpll_pin_registration *reg; 61 61 62 62 list_for_each_entry(reg, &ref->registration_list, list) { 63 - if (reg->ops == ops && reg->priv == priv) 63 + if (reg->ops == ops && reg->priv == priv && 64 + reg->cookie == cookie) 64 65 return reg; 65 66 } 66 67 return NULL; ··· 70 67 71 68 static int 72 69 dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin, 73 - const struct dpll_pin_ops *ops, void *priv) 70 + const struct dpll_pin_ops *ops, void *priv, 71 + void *cookie) 74 72 { 75 73 struct dpll_pin_registration *reg; 76 74 struct dpll_pin_ref *ref; ··· 82 78 xa_for_each(xa_pins, i, ref) { 83 79 if (ref->pin != pin) 84 80 continue; 85 - reg = dpll_pin_registration_find(ref, ops, priv); 81 + reg = dpll_pin_registration_find(ref, ops, priv, cookie); 86 82 if (reg) { 87 83 refcount_inc(&ref->refcount); 88 84 return 0; ··· 115 111 } 116 112 reg->ops = ops; 117 113 reg->priv = priv; 114 + reg->cookie = cookie; 118 115 if (ref_exists) 119 116 refcount_inc(&ref->refcount); 120 117 list_add_tail(&reg->list, &ref->registration_list); ··· 124 119 } 125 120 126 121 static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin, 127 - const struct dpll_pin_ops *ops, void *priv) 122 + const struct dpll_pin_ops *ops, void *priv, 123 + void *cookie) 128 124 { 129 125 struct dpll_pin_registration *reg; 130 126 struct dpll_pin_ref *ref; ··· 134 128 xa_for_each(xa_pins, i, ref) { 135 129 if (ref->pin != pin) 136 130 continue; 137 - reg = dpll_pin_registration_find(ref, ops, priv); 131 + reg = dpll_pin_registration_find(ref, ops, priv, cookie); 138 132 if (WARN_ON(!reg)) 139 133 return -EINVAL; 140 134 list_del(&reg->list); ··· 152 146 153 147 static int 154 148 dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll, 155 - const struct dpll_pin_ops *ops, void *priv) 149 + const struct dpll_pin_ops *ops, void *priv, void *cookie) 156 150 { 157 151 struct dpll_pin_registration *reg; 158 152 struct dpll_pin_ref *ref; ··· 163 157 xa_for_each(xa_dplls, i, ref) { 164 158 if (ref->dpll != dpll) 165 159 continue; 166 - reg = dpll_pin_registration_find(ref, ops, priv); 160 + reg = dpll_pin_registration_find(ref, ops, priv, cookie); 167 161 if (reg) { 168 162 refcount_inc(&ref->refcount); 169 163 return 0; ··· 196 190 } 197 191 reg->ops = ops; 198 192 reg->priv = priv; 193 + reg->cookie = cookie; 199 194 if (ref_exists) 200 195 refcount_inc(&ref->refcount); 201 196 list_add_tail(&reg->list, &ref->registration_list); ··· 206 199 207 200 static void 208 201 dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll, 209 - const struct dpll_pin_ops *ops, void *priv) 202 + const struct dpll_pin_ops *ops, void *priv, void *cookie) 210 203 { 211 204 struct dpll_pin_registration *reg; 212 205 struct dpll_pin_ref *ref; ··· 215 208 xa_for_each(xa_dplls, i, ref) { 216 209 if (ref->dpll != dpll) 217 210 continue; 218 - reg = dpll_pin_registration_find(ref, ops, priv); 211 + reg = dpll_pin_registration_find(ref, ops, priv, cookie); 219 212 if (WARN_ON(!reg)) 220 213 return; 221 214 list_del(&reg->list); ··· 601 594 602 595 static int 603 596 __dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin, 604 - const struct dpll_pin_ops *ops, void *priv) 597 + const struct dpll_pin_ops *ops, void *priv, void *cookie) 605 598 { 606 599 int ret; 607 600 608 - ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv); 601 + ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv, cookie); 609 602 if (ret) 610 603 return ret; 611 - ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv); 604 + ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv, cookie); 612 605 if (ret) 613 606 goto ref_pin_del; 614 607 xa_set_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); ··· 617 610 return ret; 618 611 619 612 ref_pin_del: 620 - dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv); 613 + dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); 621 614 return ret; 622 615 } 623 616 ··· 649 642 dpll->clock_id == pin->clock_id))) 650 643 ret = -EINVAL; 651 644 else 652 - ret = __dpll_pin_register(dpll, pin, ops, priv); 645 + ret = __dpll_pin_register(dpll, pin, ops, priv, NULL); 653 646 mutex_unlock(&dpll_lock); 654 647 655 648 return ret; ··· 658 651 659 652 static void 660 653 __dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin, 661 - const struct dpll_pin_ops *ops, void *priv) 654 + const struct dpll_pin_ops *ops, void *priv, void *cookie) 662 655 { 663 656 ASSERT_DPLL_PIN_REGISTERED(pin); 664 - dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv); 665 - dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv); 657 + dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv, cookie); 658 + dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv, cookie); 666 659 if (xa_empty(&pin->dpll_refs)) 667 660 xa_clear_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED); 668 661 } ··· 687 680 688 681 mutex_lock(&dpll_lock); 689 682 dpll_pin_delete_ntf(pin); 690 - __dpll_pin_unregister(dpll, pin, ops, priv); 683 + __dpll_pin_unregister(dpll, pin, ops, priv, NULL); 691 684 mutex_unlock(&dpll_lock); 692 685 } 693 686 EXPORT_SYMBOL_GPL(dpll_pin_unregister); ··· 723 716 return -EINVAL; 724 717 725 718 mutex_lock(&dpll_lock); 726 - ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv); 719 + ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv, pin); 727 720 if (ret) 728 721 goto unlock; 729 722 refcount_inc(&pin->refcount); 730 723 xa_for_each(&parent->dpll_refs, i, ref) { 731 - ret = __dpll_pin_register(ref->dpll, pin, ops, priv); 724 + ret = __dpll_pin_register(ref->dpll, pin, ops, priv, parent); 732 725 if (ret) { 733 726 stop = i; 734 727 goto dpll_unregister; ··· 742 735 dpll_unregister: 743 736 xa_for_each(&parent->dpll_refs, i, ref) 744 737 if (i < stop) { 745 - __dpll_pin_unregister(ref->dpll, pin, ops, priv); 738 + __dpll_pin_unregister(ref->dpll, pin, ops, priv, 739 + parent); 746 740 dpll_pin_delete_ntf(pin); 747 741 } 748 742 refcount_dec(&pin->refcount); 749 - dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv); 743 + dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); 750 744 unlock: 751 745 mutex_unlock(&dpll_lock); 752 746 return ret; ··· 772 764 773 765 mutex_lock(&dpll_lock); 774 766 dpll_pin_delete_ntf(pin); 775 - dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv); 767 + dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv, pin); 776 768 refcount_dec(&pin->refcount); 777 769 xa_for_each(&pin->dpll_refs, i, ref) 778 - __dpll_pin_unregister(ref->dpll, pin, ops, priv); 770 + __dpll_pin_unregister(ref->dpll, pin, ops, priv, parent); 779 771 mutex_unlock(&dpll_lock); 780 772 } 781 773 EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);