2021-06-25 09:25:40

by Yangbo Lu

[permalink] [raw]
Subject: [net-next, v4, 02/11] ptp: support ptp physical/virtual clocks conversion

Support ptp physical/virtual clocks conversion via sysfs.
There will be a new attribute n_vclocks under ptp physical
clock sysfs.

- In default, the value is 0 meaning only ptp physical clock
is in use.
- Setting the value can create corresponding number of ptp
virtual clocks to use. But current physical clock is guaranteed
to stay free running.
- Setting the value back to 0 can delete virtual clocks and back
use physical clock again.

Another new attribute max_vclocks control the maximum number of
ptp vclocks.

Signed-off-by: Yangbo Lu <[email protected]>
---
Changes for v2:
- Split from v1 patch #2.
- Converted to num_vclocks for creating virtual clocks.
- Guranteed physical clock free running when using virtual
clocks.
- Fixed build warning.
- Updated copyright.
Changes for v3:
- Protected concurrency of ptp->num_vclocks accessing.
Changes for v4:
- Rephrased description in doc.
- Used unsigned int for vclocks number, and max_vclocks
for limitiation.
- Fixed mutex locking.
- Other minor fixes.
---
Documentation/ABI/testing/sysfs-ptp | 20 ++++
drivers/ptp/ptp_clock.c | 25 +++++
drivers/ptp/ptp_private.h | 21 +++++
drivers/ptp/ptp_sysfs.c | 138 ++++++++++++++++++++++++++++
4 files changed, 204 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-ptp b/Documentation/ABI/testing/sysfs-ptp
index 2363ad810ddb..d378f57c1b73 100644
--- a/Documentation/ABI/testing/sysfs-ptp
+++ b/Documentation/ABI/testing/sysfs-ptp
@@ -33,6 +33,13 @@ Description:
frequency adjustment value (a positive integer) in
parts per billion.

+What: /sys/class/ptp/ptpN/max_vclocks
+Date: May 2021
+Contact: Yangbo Lu <[email protected]>
+Description:
+ This file contains the maximum number of ptp vclocks.
+ Write integer to re-configure it.
+
What: /sys/class/ptp/ptpN/n_alarms
Date: September 2010
Contact: Richard Cochran <[email protected]>
@@ -61,6 +68,19 @@ Description:
This file contains the number of programmable pins
offered by the PTP hardware clock.

+What: /sys/class/ptp/ptpN/n_vclocks
+Date: May 2021
+Contact: Yangbo Lu <[email protected]>
+Description:
+ This file contains the number of virtual PTP clocks in
+ use. By default, the value is 0 meaning that only the
+ physical clock is in use. Setting the value creates
+ the corresponding number of virtual clocks and causes
+ the physical clock to become free running. Setting the
+ value back to 0 deletes the virtual clocks and
+ switches the physical clock back to normal, adjustable
+ operation.
+
What: /sys/class/ptp/ptpN/pins
Date: March 2014
Contact: Richard Cochran <[email protected]>
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 841d8900504d..9fa8fea13616 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -76,6 +76,11 @@ static int ptp_clock_settime(struct posix_clock *pc, const struct timespec64 *tp
{
struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);

+ if (ptp_vclock_in_use(ptp)) {
+ pr_err("ptp: virtual clock in use\n");
+ return -EBUSY;
+ }
+
return ptp->info->settime64(ptp->info, tp);
}

@@ -97,6 +102,11 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
struct ptp_clock_info *ops;
int err = -EOPNOTSUPP;

+ if (ptp_vclock_in_use(ptp)) {
+ pr_err("ptp: virtual clock in use\n");
+ return -EBUSY;
+ }
+
ops = ptp->info;

if (tx->modes & ADJ_SETOFFSET) {
@@ -161,6 +171,7 @@ static void ptp_clock_release(struct device *dev)
ptp_cleanup_pin_groups(ptp);
mutex_destroy(&ptp->tsevq_mux);
mutex_destroy(&ptp->pincfg_mux);
+ mutex_destroy(&ptp->n_vclocks_mux);
ida_simple_remove(&ptp_clocks_map, ptp->index);
kfree(ptp);
}
@@ -208,6 +219,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
spin_lock_init(&ptp->tsevq.lock);
mutex_init(&ptp->tsevq_mux);
mutex_init(&ptp->pincfg_mux);
+ mutex_init(&ptp->n_vclocks_mux);
init_waitqueue_head(&ptp->tsev_wq);

if (ptp->info->do_aux_work) {
@@ -220,6 +232,13 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
}
}

+ /* PTP virtual clock is being registered under physical clock */
+ if (parent->class && strcmp(parent->class->name, "ptp") == 0)
+ ptp->is_virtual_clock = true;
+
+ if (!ptp->is_virtual_clock)
+ ptp->max_vclocks = PTP_DEFAULT_MAX_VCLOCKS;
+
err = ptp_populate_pin_groups(ptp);
if (err)
goto no_pin_groups;
@@ -269,6 +288,7 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
kworker_err:
mutex_destroy(&ptp->tsevq_mux);
mutex_destroy(&ptp->pincfg_mux);
+ mutex_destroy(&ptp->n_vclocks_mux);
ida_simple_remove(&ptp_clocks_map, index);
no_slot:
kfree(ptp);
@@ -279,6 +299,11 @@ EXPORT_SYMBOL(ptp_clock_register);

int ptp_clock_unregister(struct ptp_clock *ptp)
{
+ if (ptp_vclock_in_use(ptp)) {
+ pr_err("ptp: virtual clock in use\n");
+ return -EBUSY;
+ }
+
ptp->defunct = 1;
wake_up_interruptible(&ptp->tsev_wq);

diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index 853b79b6b30e..87cb55953b69 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -18,6 +18,7 @@

#define PTP_MAX_TIMESTAMPS 128
#define PTP_BUF_TIMESTAMPS 30
+#define PTP_DEFAULT_MAX_VCLOCKS 20

struct timestamp_event_queue {
struct ptp_extts_event buf[PTP_MAX_TIMESTAMPS];
@@ -46,6 +47,10 @@ struct ptp_clock {
const struct attribute_group *pin_attr_groups[2];
struct kthread_worker *kworker;
struct kthread_delayed_work aux_work;
+ unsigned int max_vclocks;
+ unsigned int n_vclocks;
+ struct mutex n_vclocks_mux; /* protect concurrent n_vclocks access */
+ bool is_virtual_clock;
};

#define info_to_vclock(d) container_of((d), struct ptp_vclock, info)
@@ -74,6 +79,22 @@ static inline int queue_cnt(struct timestamp_event_queue *q)
return cnt < 0 ? PTP_MAX_TIMESTAMPS + cnt : cnt;
}

+/* Check if ptp virtual clock is in use */
+static inline bool ptp_vclock_in_use(struct ptp_clock *ptp)
+{
+ bool in_use = false;
+
+ if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
+ return true;
+
+ if (!ptp->is_virtual_clock && ptp->n_vclocks)
+ in_use = true;
+
+ mutex_unlock(&ptp->n_vclocks_mux);
+
+ return in_use;
+}
+
/*
* see ptp_chardev.c
*/
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index be076a91e20e..0c6411409a18 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c
@@ -3,6 +3,7 @@
* PTP 1588 clock support - sysfs interface.
*
* Copyright (C) 2010 OMICRON electronics GmbH
+ * Copyright 2021 NXP
*/
#include <linux/capability.h>
#include <linux/slab.h>
@@ -148,6 +149,137 @@ static ssize_t pps_enable_store(struct device *dev,
}
static DEVICE_ATTR(pps_enable, 0220, NULL, pps_enable_store);

+static int unregister_vclock(struct device *dev, void *data)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct ptp_clock_info *info = ptp->info;
+ struct ptp_vclock *vclock;
+ u8 *num = data;
+
+ vclock = info_to_vclock(info);
+ dev_info(dev->parent, "delete virtual clock ptp%d\n",
+ vclock->clock->index);
+
+ ptp_vclock_unregister(vclock);
+ (*num)--;
+
+ /* For break. Not error. */
+ if (*num == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t n_vclocks_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ ssize_t size;
+
+ if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
+ return -ERESTARTSYS;
+
+ size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->n_vclocks);
+
+ mutex_unlock(&ptp->n_vclocks_mux);
+
+ return size;
+}
+
+static ssize_t n_vclocks_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ struct ptp_vclock *vclock;
+ int err = -EINVAL;
+ u32 num, i;
+
+ if (kstrtou32(buf, 0, &num))
+ return err;
+
+ if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
+ return -ERESTARTSYS;
+
+ if (num > ptp->max_vclocks) {
+ dev_err(dev, "max value is %d\n", ptp->max_vclocks);
+ goto out;
+ }
+
+ /* Need to create more vclocks */
+ if (num > ptp->n_vclocks) {
+ for (i = 0; i < num - ptp->n_vclocks; i++) {
+ vclock = ptp_vclock_register(ptp);
+ if (!vclock)
+ goto out;
+
+ dev_info(dev, "new virtual clock ptp%d\n",
+ vclock->clock->index);
+ }
+ }
+
+ /* Need to delete vclocks */
+ if (num < ptp->n_vclocks) {
+ i = ptp->n_vclocks - num;
+ device_for_each_child_reverse(dev, &i,
+ unregister_vclock);
+ }
+
+ if (num == 0)
+ dev_info(dev, "only physical clock in use now\n");
+ else
+ dev_info(dev, "guarantee physical clock free running\n");
+
+ ptp->n_vclocks = num;
+ mutex_unlock(&ptp->n_vclocks_mux);
+
+ return count;
+out:
+ mutex_unlock(&ptp->n_vclocks_mux);
+ return err;
+}
+static DEVICE_ATTR_RW(n_vclocks);
+
+static ssize_t max_vclocks_show(struct device *dev,
+ struct device_attribute *attr, char *page)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ ssize_t size;
+
+ size = snprintf(page, PAGE_SIZE - 1, "%d\n", ptp->max_vclocks);
+
+ return size;
+}
+
+static ssize_t max_vclocks_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ptp_clock *ptp = dev_get_drvdata(dev);
+ u32 max;
+
+ if (kstrtou32(buf, 0, &max) || max == 0)
+ return -EINVAL;
+
+ if (max == ptp->max_vclocks)
+ return count;
+
+ if (mutex_lock_interruptible(&ptp->n_vclocks_mux))
+ return -ERESTARTSYS;
+
+ if (max < ptp->n_vclocks) {
+ mutex_unlock(&ptp->n_vclocks_mux);
+ return -EINVAL;
+ }
+
+ ptp->max_vclocks = max;
+
+ mutex_unlock(&ptp->n_vclocks_mux);
+
+ return count;
+}
+static DEVICE_ATTR_RW(max_vclocks);
+
static struct attribute *ptp_attrs[] = {
&dev_attr_clock_name.attr,

@@ -162,6 +294,8 @@ static struct attribute *ptp_attrs[] = {
&dev_attr_fifo.attr,
&dev_attr_period.attr,
&dev_attr_pps_enable.attr,
+ &dev_attr_n_vclocks.attr,
+ &dev_attr_max_vclocks.attr,
NULL
};

@@ -183,6 +317,10 @@ static umode_t ptp_is_attribute_visible(struct kobject *kobj,
} else if (attr == &dev_attr_pps_enable.attr) {
if (!info->pps)
mode = 0;
+ } else if (attr == &dev_attr_n_vclocks.attr ||
+ attr == &dev_attr_max_vclocks.attr) {
+ if (ptp->is_virtual_clock)
+ mode = 0;
}

return mode;
--
2.25.1


2021-06-27 09:37:35

by kernel test robot

[permalink] [raw]
Subject: [ptp] d7b8e363d0: BUG:kernel_NULL_pointer_dereference,address



Greeting,

FYI, we noticed the following commit (built with gcc-9):

commit: d7b8e363d025cb61b9fbcee829ce69ff82393e78 ("[net-next, v4, 02/11] ptp: support ptp physical/virtual clocks conversion")
url: https://github.com/0day-ci/linux/commits/Yangbo-Lu/ptp-support-virtual-clocks-and-timestamping/20210625-172554


in testcase: boot

on test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):


+-------------------------------------------------+------------+------------+
| | fd3836e9bc | d7b8e363d0 |
+-------------------------------------------------+------------+------------+
| boot_successes | 17 | 0 |
| BUG:kernel_NULL_pointer_dereference,address | 0 | 35 |
| Oops:#[##] | 0 | 35 |
| EIP:ptp_clock_register | 0 | 35 |
| Kernel_panic-not_syncing:Fatal_exception | 0 | 35 |
+-------------------------------------------------+------------+------------+


If you fix the issue, kindly add following tag
Reported-by: kernel test robot <[email protected]>


[ 1.357423] BUG: kernel NULL pointer dereference, address: 00000190
[ 1.358353] #PF: supervisor read access in kernel mode
[ 1.358353] #PF: error_code(0x0000) - not-present page
[ 1.358353] *pde = 00000000
[ 1.358353] Oops: 0000 [#1] SMP
[ 1.358353] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.13.0-rc6-02069-gd7b8e363d025 #1
[ 1.363334] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014
[ 1.363334] EIP: ptp_clock_register (drivers/ptp/ptp_clock.c:236)
[ 1.363334] Code: ff ff b3 34 02 00 00 68 e3 7a f9 ca 6a 00 e8 28 29 8e ff 89 83 a8 12 00 00 83 c4 14 3d 00 f0 ff ff 0f 87 07 95 36 00 8b 45 9c <8b> 80 90 01 00 00 85 c0 0f 84 33 01 00 00 8b 00 ba 9c 7a f9 ca e8
All code
========
0: ff (bad)
1: ff b3 34 02 00 00 pushq 0x234(%rbx)
7: 68 e3 7a f9 ca pushq $0xffffffffcaf97ae3
c: 6a 00 pushq $0x0
e: e8 28 29 8e ff callq 0xffffffffff8e293b
13: 89 83 a8 12 00 00 mov %eax,0x12a8(%rbx)
19: 83 c4 14 add $0x14,%esp
1c: 3d 00 f0 ff ff cmp $0xfffff000,%eax
21: 0f 87 07 95 36 00 ja 0x36952e
27: 8b 45 9c mov -0x64(%rbp),%eax
2a:* 8b 80 90 01 00 00 mov 0x190(%rax),%eax <-- trapping instruction
30: 85 c0 test %eax,%eax
32: 0f 84 33 01 00 00 je 0x16b
38: 8b 00 mov (%rax),%eax
3a: ba 9c 7a f9 ca mov $0xcaf97a9c,%edx
3f: e8 .byte 0xe8

Code starting with the faulting instruction
===========================================
0: 8b 80 90 01 00 00 mov 0x190(%rax),%eax
6: 85 c0 test %eax,%eax
8: 0f 84 33 01 00 00 je 0x141
e: 8b 00 mov (%rax),%eax
10: ba 9c 7a f9 ca mov $0xcaf97a9c,%edx
15: e8 .byte 0xe8
[ 1.363334] EAX: 00000000 EBX: c12bc000 ECX: 00000000 EDX: c12bd278
[ 1.363334] ESI: cb67d904 EDI: 0fc00000 EBP: c10c5f1c ESP: c10c5eb4
[ 1.363334] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 EFLAGS: 00010246
[ 1.363334] CR0: 80050033 CR2: 00000190 CR3: 0b540000 CR4: 000406d0
[ 1.363334] Call Trace:
[ 1.363334] ? kobject_uevent (lib/kobject_uevent.c:643)
[ 1.363334] ? ptp_init (drivers/ptp/ptp_kvm_common.c:136)
[ 1.363334] ? slow_virt_to_phys (arch/x86/mm/pat/set_memory.c:704)
[ 1.363334] ptp_kvm_init (include/linux/err.h:31 include/linux/err.h:60 drivers/ptp/ptp_kvm_common.c:150)
[ 1.363334] ? ptp_init (drivers/ptp/ptp_kvm_common.c:136)
[ 1.363334] do_one_initcall (init/main.c:1249)
[ 1.363334] ? rdinit_setup (init/main.c:1308)
[ 1.363334] kernel_init_freeable (init/main.c:1321 init/main.c:1338 init/main.c:1358 init/main.c:1560)
[ 1.363334] ? rest_init (init/main.c:1444)
[ 1.363334] kernel_init (init/main.c:1449)
[ 1.363334] ret_from_fork (arch/x86/entry/entry_32.S:775)
[ 1.363334] Modules linked in:
[ 1.363334] CR2: 0000000000000190
[ 1.363334] ---[ end trace 3b8fb0506f39eed9 ]---
[ 1.363334] EIP: ptp_clock_register (drivers/ptp/ptp_clock.c:236)
[ 1.363334] Code: ff ff b3 34 02 00 00 68 e3 7a f9 ca 6a 00 e8 28 29 8e ff 89 83 a8 12 00 00 83 c4 14 3d 00 f0 ff ff 0f 87 07 95 36 00 8b 45 9c <8b> 80 90 01 00 00 85 c0 0f 84 33 01 00 00 8b 00 ba 9c 7a f9 ca e8
All code
========
0: ff (bad)
1: ff b3 34 02 00 00 pushq 0x234(%rbx)
7: 68 e3 7a f9 ca pushq $0xffffffffcaf97ae3
c: 6a 00 pushq $0x0
e: e8 28 29 8e ff callq 0xffffffffff8e293b
13: 89 83 a8 12 00 00 mov %eax,0x12a8(%rbx)
19: 83 c4 14 add $0x14,%esp
1c: 3d 00 f0 ff ff cmp $0xfffff000,%eax
21: 0f 87 07 95 36 00 ja 0x36952e
27: 8b 45 9c mov -0x64(%rbp),%eax
2a:* 8b 80 90 01 00 00 mov 0x190(%rax),%eax <-- trapping instruction
30: 85 c0 test %eax,%eax
32: 0f 84 33 01 00 00 je 0x16b
38: 8b 00 mov (%rax),%eax
3a: ba 9c 7a f9 ca mov $0xcaf97a9c,%edx
3f: e8 .byte 0xe8

Code starting with the faulting instruction
===========================================
0: 8b 80 90 01 00 00 mov 0x190(%rax),%eax
6: 85 c0 test %eax,%eax
8: 0f 84 33 01 00 00 je 0x141
e: 8b 00 mov (%rax),%eax
10: ba 9c 7a f9 ca mov $0xcaf97a9c,%edx
15: e8 .byte 0xe8


To reproduce:

# build kernel
cd linux
cp config-5.13.0-rc6-02069-gd7b8e363d025 .config
make HOSTCC=gcc-9 CC=gcc-9 ARCH=i386 olddefconfig prepare modules_prepare bzImage

git clone https://github.com/intel/lkp-tests.git
cd lkp-tests
bin/lkp qemu -k <bzImage> job-script # job-script is attached in this email



---
0DAY/LKP+ Test Infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/[email protected] Intel Corporation

Thanks,
Oliver Sang


Attachments:
(No filename) (6.33 kB)
config-5.13.0-rc6-02069-gd7b8e363d025 (127.88 kB)
job-script (4.50 kB)
dmesg.xz (9.32 kB)
Download all attachments