Commit 94fa82b0 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'dpll-fixes'



Arkadiusz Kubalewski says:

====================
dpll: fix unordered unbind/bind registerer issues

Fix issues when performing unordered unbind/bind of a kernel modules
which are using a dpll device with DPLL_PIN_TYPE_MUX pins.
Currently only serialized bind/unbind of such use case works, fix
the issues and allow for unserialized kernel module bind order.

The issues are observed on the ice driver, i.e.,

$ echo 0000:af:00.0 > /sys/bus/pci/drivers/ice/unbind
$ echo 0000:af:00.1 > /sys/bus/pci/drivers/ice/unbind

results in:

ice 0000:af:00.0: Removed PTP clock
BUG: kernel NULL pointer dereference, address: 0000000000000010
PF: supervisor read access in kernel mode
PF: error_code(0x0000) - not-present page
PGD 0 P4D 0
Oops: 0000 [#1] PREEMPT SMP PTI
CPU: 7 PID: 71848 Comm: bash Kdump: loaded Not tainted 6.6.0-rc5_next-queue_19th-Oct-2023-01625-g039e5d15e451 #1
Hardware name: Intel Corporation S2600STB/S2600STB, BIOS SE5C620.86B.02.01.0008.031920191559 03/19/2019
RIP: 0010:ice_dpll_rclk_state_on_pin_get+0x2f/0x90 [ice]
Code: 41 57 4d 89 cf 41 56 41 55 4d 89 c5 41 54 55 48 89 f5 53 4c 8b 66 08 48 89 cb 4d 8d b4 24 f0 49 00 00 4c 89 f7 e8 71 ec 1f c5 <0f> b6 5b 10 41 0f b6 84 24 30 4b 00 00 29 c3 41 0f b6 84 24 28 4b
RSP: 0018:ffffc902b179fb60 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
RDX: ffff8882c1398000 RSI: ffff888c7435cc60 RDI: ffff888c7435cb90
RBP: ffff888c7435cc60 R08: ffffc902b179fbb0 R09: 0000000000000000
R10: ffff888ef1fc8050 R11: fffffffffff82700 R12: ffff888c743581a0
R13: ffffc902b179fbb0 R14: ffff888c7435cb90 R15: 0000000000000000
FS:  00007fdc7dae0740(0000) GS:ffff888c105c0000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000010 CR3: 0000000132c24002 CR4: 00000000007706e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
PKRU: 55555554
Call Trace:
 <TASK>
 ? __die+0x20/0x70
 ? page_fault_oops+0x76/0x170
 ? exc_page_fault+0x65/0x150
 ? asm_exc_page_fault+0x22/0x30
 ? ice_dpll_rclk_state_on_pin_get+0x2f/0x90 [ice]
 ? __pfx_ice_dpll_rclk_state_on_pin_get+0x10/0x10 [ice]
 dpll_msg_add_pin_parents+0x142/0x1d0
 dpll_pin_event_send+0x7d/0x150
 dpll_pin_on_pin_unregister+0x3f/0x100
 ice_dpll_deinit_pins+0xa1/0x230 [ice]
 ice_dpll_deinit+0x29/0xe0 [ice]
 ice_remove+0xcd/0x200 [ice]
 pci_device_remove+0x33/0xa0
 device_release_driver_internal+0x193/0x200
 unbind_store+0x9d/0xb0
 kernfs_fop_write_iter+0x128/0x1c0
 vfs_write+0x2bb/0x3e0
 ksys_write+0x5f/0xe0
 do_syscall_64+0x59/0x90
 ? filp_close+0x1b/0x30
 ? do_dup2+0x7d/0xd0
 ? syscall_exit_work+0x103/0x130
 ? syscall_exit_to_user_mode+0x22/0x40
 ? do_syscall_64+0x69/0x90
 ? syscall_exit_work+0x103/0x130
 ? syscall_exit_to_user_mode+0x22/0x40
 ? do_syscall_64+0x69/0x90
 entry_SYSCALL_64_after_hwframe+0x6e/0xd8
RIP: 0033:0x7fdc7d93eb97
Code: 0b 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24
RSP: 002b:00007fff2aa91028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 000000000000000d RCX: 00007fdc7d93eb97
RDX: 000000000000000d RSI: 00005644814ec9b0 RDI: 0000000000000001
RBP: 00005644814ec9b0 R08: 0000000000000000 R09: 00007fdc7d9b14e0
R10: 00007fdc7d9b13e0 R11: 0000000000000246 R12: 000000000000000d
R13: 00007fdc7d9fb780 R14: 000000000000000d R15: 00007fdc7d9f69e0
 </TASK>
Modules linked in: uinput vfio_pci vfio_pci_core vfio_iommu_type1 vfio irqbypass ixgbevf snd_seq_dummy snd_hrtimer snd_seq snd_timer snd_seq_device snd soundcore overlay qrtr rfkill vfat fat xfs libcrc32c rpcrdma sunrpc rdma_ucm ib_srpt ib_isert iscsi_target_mod target_core_mod ib_iser libiscsi scsi_transport_iscsi rdma_cm iw_cm ib_cm intel_rapl_msr intel_rapl_common intel_uncore_frequency intel_uncore_frequency_common isst_if_common skx_edac nfit libnvdimm ipmi_ssif x86_pkg_temp_thermal intel_powerclamp coretemp irdma rapl intel_cstate ib_uverbs iTCO_wdt iTCO_vendor_support acpi_ipmi intel_uncore mei_me ipmi_si pcspkr i2c_i801 ib_core mei ipmi_devintf intel_pch_thermal ioatdma i2c_smbus ipmi_msghandler lpc_ich joydev acpi_power_meter acpi_pad ext4 mbcache jbd2 sd_mod t10_pi sg ast i2c_algo_bit drm_shmem_helper drm_kms_helper ice crct10dif_pclmul ixgbe crc32_pclmul drm crc32c_intel ahci i40e libahci ghash_clmulni_intel libata mdio dca gnss wmi fuse [last unloaded: iavf]
CR2: 0000000000000010

v6:
- fix memory corruption on error path in patch [v5 2/4]
====================

Acked-by: default avatarVadim Fedorenko <vadim.fedorenko@linux.dev>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ef485216 7dc5b18f
Loading
Loading
Loading
Loading
+57 −11
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@ static u32 dpll_pin_xa_id;
	WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
#define ASSERT_DPLL_NOT_REGISTERED(d)	\
	WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
#define ASSERT_PIN_REGISTERED(p)	\
	WARN_ON_ONCE(!xa_get_mark(&dpll_pin_xa, (p)->id, DPLL_REGISTERED))

struct dpll_device_registration {
	struct list_head list;
@@ -425,6 +423,53 @@ void dpll_device_unregister(struct dpll_device *dpll,
}
EXPORT_SYMBOL_GPL(dpll_device_unregister);

static void dpll_pin_prop_free(struct dpll_pin_properties *prop)
{
	kfree(prop->package_label);
	kfree(prop->panel_label);
	kfree(prop->board_label);
	kfree(prop->freq_supported);
}

static int dpll_pin_prop_dup(const struct dpll_pin_properties *src,
			     struct dpll_pin_properties *dst)
{
	memcpy(dst, src, sizeof(*dst));
	if (src->freq_supported && src->freq_supported_num) {
		size_t freq_size = src->freq_supported_num *
				   sizeof(*src->freq_supported);
		dst->freq_supported = kmemdup(src->freq_supported,
					      freq_size, GFP_KERNEL);
		if (!src->freq_supported)
			return -ENOMEM;
	}
	if (src->board_label) {
		dst->board_label = kstrdup(src->board_label, GFP_KERNEL);
		if (!dst->board_label)
			goto err_board_label;
	}
	if (src->panel_label) {
		dst->panel_label = kstrdup(src->panel_label, GFP_KERNEL);
		if (!dst->panel_label)
			goto err_panel_label;
	}
	if (src->package_label) {
		dst->package_label = kstrdup(src->package_label, GFP_KERNEL);
		if (!dst->package_label)
			goto err_package_label;
	}

	return 0;

err_package_label:
	kfree(dst->panel_label);
err_panel_label:
	kfree(dst->board_label);
err_board_label:
	kfree(dst->freq_supported);
	return -ENOMEM;
}

static struct dpll_pin *
dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
	       const struct dpll_pin_properties *prop)
@@ -441,20 +486,24 @@ dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
	if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
		    prop->type > DPLL_PIN_TYPE_MAX)) {
		ret = -EINVAL;
		goto err;
		goto err_pin_prop;
	}
	pin->prop = prop;
	ret = dpll_pin_prop_dup(prop, &pin->prop);
	if (ret)
		goto err_pin_prop;
	refcount_set(&pin->refcount, 1);
	xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
	xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
	ret = xa_alloc_cyclic(&dpll_pin_xa, &pin->id, pin, xa_limit_32b,
			      &dpll_pin_xa_id, GFP_KERNEL);
	if (ret)
		goto err;
		goto err_xa_alloc;
	return pin;
err:
err_xa_alloc:
	xa_destroy(&pin->dpll_refs);
	xa_destroy(&pin->parent_refs);
	dpll_pin_prop_free(&pin->prop);
err_pin_prop:
	kfree(pin);
	return ERR_PTR(ret);
}
@@ -514,6 +563,7 @@ void dpll_pin_put(struct dpll_pin *pin)
		xa_destroy(&pin->dpll_refs);
		xa_destroy(&pin->parent_refs);
		xa_erase(&dpll_pin_xa, pin->id);
		dpll_pin_prop_free(&pin->prop);
		kfree(pin);
	}
	mutex_unlock(&dpll_lock);
@@ -564,8 +614,6 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
	    WARN_ON(!ops->state_on_dpll_get) ||
	    WARN_ON(!ops->direction_get))
		return -EINVAL;
	if (ASSERT_DPLL_REGISTERED(dpll))
		return -EINVAL;

	mutex_lock(&dpll_lock);
	if (WARN_ON(!(dpll->module == pin->module &&
@@ -636,15 +684,13 @@ int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
	unsigned long i, stop;
	int ret;

	if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
	if (WARN_ON(parent->prop.type != DPLL_PIN_TYPE_MUX))
		return -EINVAL;

	if (WARN_ON(!ops) ||
	    WARN_ON(!ops->state_on_pin_get) ||
	    WARN_ON(!ops->direction_get))
		return -EINVAL;
	if (ASSERT_PIN_REGISTERED(parent))
		return -EINVAL;

	mutex_lock(&dpll_lock);
	ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv);
+2 −2
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ struct dpll_device {
 * @module:		module of creator
 * @dpll_refs:		hold referencees to dplls pin was registered with
 * @parent_refs:	hold references to parent pins pin was registered with
 * @prop:		pointer to pin properties given by registerer
 * @prop:		pin properties copied from the registerer
 * @rclk_dev_name:	holds name of device when pin can recover clock from it
 * @refcount:		refcount
 **/
@@ -55,7 +55,7 @@ struct dpll_pin {
	struct module *module;
	struct xarray dpll_refs;
	struct xarray parent_refs;
	const struct dpll_pin_properties *prop;
	struct dpll_pin_properties prop;
	refcount_t refcount;
};

+41 −16
Original line number Diff line number Diff line
@@ -303,17 +303,17 @@ dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
	if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq,
			  DPLL_A_PIN_PAD))
		return -EMSGSIZE;
	for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
	for (fs = 0; fs < pin->prop.freq_supported_num; fs++) {
		nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
		if (!nest)
			return -EMSGSIZE;
		freq = pin->prop->freq_supported[fs].min;
		freq = pin->prop.freq_supported[fs].min;
		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
				  &freq, DPLL_A_PIN_PAD)) {
			nla_nest_cancel(msg, nest);
			return -EMSGSIZE;
		}
		freq = pin->prop->freq_supported[fs].max;
		freq = pin->prop.freq_supported[fs].max;
		if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
				  &freq, DPLL_A_PIN_PAD)) {
			nla_nest_cancel(msg, nest);
@@ -329,9 +329,9 @@ static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
{
	int fs;

	for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
		if (freq >= pin->prop->freq_supported[fs].min &&
		    freq <= pin->prop->freq_supported[fs].max)
	for (fs = 0; fs < pin->prop.freq_supported_num; fs++)
		if (freq >= pin->prop.freq_supported[fs].min &&
		    freq <= pin->prop.freq_supported[fs].max)
			return true;
	return false;
}
@@ -421,7 +421,7 @@ static int
dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
		     struct netlink_ext_ack *extack)
{
	const struct dpll_pin_properties *prop = pin->prop;
	const struct dpll_pin_properties *prop = &pin->prop;
	struct dpll_pin_ref *ref;
	int ret;

@@ -553,6 +553,24 @@ __dpll_device_change_ntf(struct dpll_device *dpll)
	return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
}

static bool dpll_pin_available(struct dpll_pin *pin)
{
	struct dpll_pin_ref *par_ref;
	unsigned long i;

	if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))
		return false;
	xa_for_each(&pin->parent_refs, i, par_ref)
		if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,
				DPLL_REGISTERED))
			return true;
	xa_for_each(&pin->dpll_refs, i, par_ref)
		if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,
				DPLL_REGISTERED))
			return true;
	return false;
}

/**
 * dpll_device_change_ntf - notify that the dpll device has been changed
 * @dpll: registered dpll pointer
@@ -579,7 +597,7 @@ dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
	int ret = -ENOMEM;
	void *hdr;

	if (WARN_ON(!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED)))
	if (!dpll_pin_available(pin))
		return -ENODEV;

	msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
@@ -717,7 +735,7 @@ dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
	int ret;

	if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
	      pin->prop->capabilities)) {
	      pin->prop.capabilities)) {
		NL_SET_ERR_MSG(extack, "state changing is not allowed");
		return -EOPNOTSUPP;
	}
@@ -753,7 +771,7 @@ dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
	int ret;

	if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
	      pin->prop->capabilities)) {
	      pin->prop.capabilities)) {
		NL_SET_ERR_MSG(extack, "state changing is not allowed");
		return -EOPNOTSUPP;
	}
@@ -780,7 +798,7 @@ dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
	int ret;

	if (!(DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE &
	      pin->prop->capabilities)) {
	      pin->prop.capabilities)) {
		NL_SET_ERR_MSG(extack, "prio changing is not allowed");
		return -EOPNOTSUPP;
	}
@@ -808,7 +826,7 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
	int ret;

	if (!(DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE &
	      pin->prop->capabilities)) {
	      pin->prop.capabilities)) {
		NL_SET_ERR_MSG(extack, "direction changing is not allowed");
		return -EOPNOTSUPP;
	}
@@ -838,8 +856,8 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
	int ret;

	phase_adj = nla_get_s32(phase_adj_attr);
	if (phase_adj > pin->prop->phase_range.max ||
	    phase_adj < pin->prop->phase_range.min) {
	if (phase_adj > pin->prop.phase_range.max ||
	    phase_adj < pin->prop.phase_range.min) {
		NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
				    "phase adjust value not supported");
		return -EINVAL;
@@ -1023,7 +1041,7 @@ dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
	unsigned long i;

	xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {
		prop = pin->prop;
		prop = &pin->prop;
		cid_match = clock_id ? pin->clock_id == clock_id : true;
		mod_match = mod_name_attr && module_name(pin->module) ?
			!nla_strcmp(mod_name_attr,
@@ -1130,6 +1148,10 @@ int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
	}
	pin = dpll_pin_find_from_nlattr(info);
	if (!IS_ERR(pin)) {
		if (!dpll_pin_available(pin)) {
			nlmsg_free(msg);
			return -ENODEV;
		}
		ret = dpll_msg_add_pin_handle(msg, pin);
		if (ret) {
			nlmsg_free(msg);
@@ -1179,6 +1201,8 @@ int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)

	xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,
				 ctx->idx) {
		if (!dpll_pin_available(pin))
			continue;
		hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
				  cb->nlh->nlmsg_seq,
				  &dpll_nl_family, NLM_F_MULTI,
@@ -1441,7 +1465,8 @@ int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
	}
	info->user_ptr[0] = xa_load(&dpll_pin_xa,
				    nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
	if (!info->user_ptr[0]) {
	if (!info->user_ptr[0] ||
	    !dpll_pin_available(info->user_ptr[0])) {
		NL_SET_ERR_MSG(info->extack, "pin not found");
		ret = -ENODEV;
		goto unlock_dev;