diff mbox series

ptp: Demultiplexed timestamp channels

Message ID 20230830214101.509086-2-reibax@gmail.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series ptp: Demultiplexed timestamp channels | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 1330 this patch: 22
netdev/cc_maintainers success CCed 2 of 2 maintainers
netdev/build_clang fail Errors and warnings before: 1353 this patch: 21
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 1353 this patch: 22
netdev/checkpatch warning CHECK: Lines should not end with a '(' CHECK: struct mutex definition without comment WARNING: line length of 89 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Xabier Marquiegui Aug. 30, 2023, 9:41 p.m. UTC
Add the posibility to demultiplex the timestamp channels for
external timestamp event channels.

In some applications it can be necessary to have different
consumers for different timestamp channels. For example,
synchronize to an external pps source with linuxptp ts2phc
while timestmping external events with another application.

This commit maintains the original multiplexed queue and adds an
individual queue per external timestamp channel. All enabled channels
will be directed to the multiplexed queue by default. On file open, a
specific channel will be redirected to the dedicated char device, and on
close it will automatically go back to the multiplexed queue.

Signed-off-by: Xabier Marquiegui <reibax@gmail.com>
---
 drivers/ptp/ptp_chardev.c | 167 +++++++++++++++++++++++++++++++++++---
 drivers/ptp/ptp_clock.c   |  43 +++++++---
 drivers/ptp/ptp_private.h |  22 +++++
 3 files changed, 210 insertions(+), 22 deletions(-)

Comments

Richard Cochran Aug. 30, 2023, 10:01 p.m. UTC | #1
On Wed, Aug 30, 2023 at 11:41:01PM +0200, Xabier Marquiegui wrote:

> +ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
> +		       loff_t *offset)
> +{
> +	struct ptp_dmtsc_cdev_info *cdev = file->private_data;
> +
> +	return ptp_queue_read(cdev->pclock, buf, cnt, cdev->minor);
> +}
> +
> +static const struct file_operations fops = {
> +						.owner = THIS_MODULE,
> +						.open = ptp_dmtsc_open,
> +						.read = ptp_dmtsc_read,
> +						.release = ptp_dmtsc_release
> +						};

I'll say it again... There is no need to add a new char dev!

You will need a patch series of at least three patches, step by step:

1. Delete ptp_clock.tsevq
   Replace it will a linked list of `struct timestamp_event_queue`
   Populate the list with ONE queue in ptp_clock_register()

   This will be your first patch.
   It will have the same functionality as before.

2. Allocate a new `struct timestamp_event_queue` on posix_clock_operations.open()
   Add it into the list.
   Remove it again on posix_clock_operations.release()

   This will introduce new functionality, as all events will go to all
   consumers.

3. Introduce a new ioctl that filters events based on user preference.

Hm?

Thanks,
Richard
kernel test robot Aug. 31, 2023, 12:29 a.m. UTC | #2
Hi Xabier,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net/main]
[also build test WARNING on net-next/main linus/master horms-ipvs/master v6.5 next-20230830]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xabier-Marquiegui/ptp-Demultiplexed-timestamp-channels/20230831-054428
base:   net/main
patch link:    https://lore.kernel.org/r/20230830214101.509086-2-reibax%40gmail.com
patch subject: [PATCH] ptp: Demultiplexed timestamp channels
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20230831/202308310809.wcvhamyw-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230831/202308310809.wcvhamyw-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202308310809.wcvhamyw-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/ptp/ptp_chardev.c:449:9: warning: no previous prototype for 'ptp_queue_read' [-Wmissing-prototypes]
     449 | ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
         |         ^~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:544:5: warning: no previous prototype for 'ptp_dmtsc_release' [-Wmissing-prototypes]
     544 | int ptp_dmtsc_release(struct inode *inode, struct file *file)
         |     ^~~~~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:556:9: warning: no previous prototype for 'ptp_dmtsc_read' [-Wmissing-prototypes]
     556 | ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
         |         ^~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:571:6: warning: no previous prototype for 'ptp_dmtsc_cdev_clean' [-Wmissing-prototypes]
     571 | void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
         |      ^~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/linkage.h:7,
                    from include/linux/preempt.h:10,
                    from arch/m68k/include/asm/irqflags.h:6,
                    from include/linux/irqflags.h:17,
                    from arch/m68k/include/asm/atomic.h:6,
                    from include/linux/atomic.h:7,
                    from include/linux/mm_types_task.h:13,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/ptp/ptp_chardev.c:7:
   drivers/ptp/ptp_chardev.c: In function 'ptp_dmtsc_dev_register':
   include/linux/export.h:31:21: error: passing argument 1 of 'class_create' from incompatible pointer type [-Werror=incompatible-pointer-types]
      31 | #define THIS_MODULE ((struct module *)0)
         |                     ^~~~~~~~~~~~~~~~~~~~
         |                     |
         |                     struct module *
   drivers/ptp/ptp_chardev.c:612:38: note: in expansion of macro 'THIS_MODULE'
     612 |                         class_create(THIS_MODULE, "ptptsevqch_class");
         |                                      ^~~~~~~~~~~
   In file included from include/linux/device.h:31,
                    from include/linux/cdev.h:8,
                    from include/linux/posix-clock.h:10,
                    from drivers/ptp/ptp_chardev.c:8:
   include/linux/device/class.h:230:54: note: expected 'const char *' but argument is of type 'struct module *'
     230 | struct class * __must_check class_create(const char *name);
         |                                          ~~~~~~~~~~~~^~~~
   drivers/ptp/ptp_chardev.c:612:25: error: too many arguments to function 'class_create'
     612 |                         class_create(THIS_MODULE, "ptptsevqch_class");
         |                         ^~~~~~~~~~~~
   include/linux/device/class.h:230:29: note: declared here
     230 | struct class * __must_check class_create(const char *name);
         |                             ^~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/ptp_queue_read +449 drivers/ptp/ptp_chardev.c

   448	
 > 449	ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
   450			       int dmtsc)
   451	{
   452		struct timestamp_event_queue *queue;
   453		struct mutex *tsevq_mux;
   454		struct ptp_extts_event *event;
   455		unsigned long flags;
   456		size_t qcnt, i;
   457		int result;
   458	
   459		if (dmtsc < 0) {
   460			queue = &ptp->tsevq;
   461			tsevq_mux = &ptp->tsevq_mux;
   462		} else {
   463			queue = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq;
   464			tsevq_mux = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq_mux;
   465		}
   466	
   467		if (cnt % sizeof(struct ptp_extts_event) != 0)
   468			return -EINVAL;
   469	
   470		if (cnt > EXTTS_BUFSIZE)
   471			cnt = EXTTS_BUFSIZE;
   472	
   473		cnt = cnt / sizeof(struct ptp_extts_event);
   474	
   475		if (mutex_lock_interruptible(tsevq_mux))
   476			return -ERESTARTSYS;
   477	
   478		if (wait_event_interruptible(ptp->tsev_wq,
   479					     ptp->defunct || queue_cnt(queue))) {
   480			mutex_unlock(tsevq_mux);
   481			return -ERESTARTSYS;
   482		}
   483	
   484		if (ptp->defunct) {
   485			mutex_unlock(tsevq_mux);
   486			return -ENODEV;
   487		}
   488	
   489		event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
   490		if (!event) {
   491			mutex_unlock(tsevq_mux);
   492			return -ENOMEM;
   493		}
   494	
   495		spin_lock_irqsave(&queue->lock, flags);
   496	
   497		qcnt = queue_cnt(queue);
   498	
   499		if (cnt > qcnt)
   500			cnt = qcnt;
   501	
   502		for (i = 0; i < cnt; i++) {
   503			event[i] = queue->buf[queue->head];
   504			queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
   505		}
   506	
   507		spin_unlock_irqrestore(&queue->lock, flags);
   508	
   509		cnt = cnt * sizeof(struct ptp_extts_event);
   510	
   511		mutex_unlock(tsevq_mux);
   512	
   513		result = cnt;
   514		if (copy_to_user(buf, event, cnt))
   515			result = -EFAULT;
   516	
   517		kfree(event);
   518		return result;
   519	}
   520	
   521	ssize_t ptp_read(struct posix_clock *pc, uint rdflags, char __user *buf,
   522			 size_t cnt)
   523	{
   524		struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
   525	
   526		return ptp_queue_read(ptp, buf, cnt, DMTSC_NOT);
   527	}
   528	
   529	static int ptp_dmtsc_open(struct inode *inode, struct file *file)
   530	{
   531		struct ptp_dmtsc_cdev_info *cdev = container_of(
   532			inode->i_cdev, struct ptp_dmtsc_cdev_info, dmtsc_cdev);
   533	
   534		file->private_data = cdev;
   535	
   536		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   537			return -ERESTARTSYS;
   538		cdev->pclock->dmtsc_en_flags |= (0x1 << (cdev->minor));
   539		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   540	
   541		return stream_open(inode, file);
   542	}
   543	
 > 544	int ptp_dmtsc_release(struct inode *inode, struct file *file)
   545	{
   546		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   547	
   548		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   549			return -ERESTARTSYS;
   550		cdev->pclock->dmtsc_en_flags &= ~(0x1 << (cdev->minor));
   551		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   552	
   553		return 0;
   554	}
   555	
 > 556	ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
   557			       loff_t *offset)
   558	{
   559		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   560	
   561		return ptp_queue_read(cdev->pclock, buf, cnt, cdev->minor);
   562	}
   563	
   564	static const struct file_operations fops = {
   565							.owner = THIS_MODULE,
   566							.open = ptp_dmtsc_open,
   567							.read = ptp_dmtsc_read,
   568							.release = ptp_dmtsc_release
   569							};
   570	
 > 571	void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
   572	{
   573		int idx, major;
   574		dev_t device;
   575	
   576		major = MAJOR(ptp->dmtsc_devs.devid);
   577		for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
   578			if (ptp->dmtsc_devs.cdev_info[idx].minor >= 0) {
   579				device = MKDEV(major, idx);
   580				device_destroy(ptp->dmtsc_devs.dmtsc_class, device);
   581				cdev_del(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev);
   582				ptp->dmtsc_devs.cdev_info[idx].minor = -1;
   583			}
   584		}
   585		class_destroy(ptp->dmtsc_devs.dmtsc_class);
   586		unregister_chrdev_region(ptp->dmtsc_devs.devid, ptp->info->n_ext_ts);
   587		mutex_destroy(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
   588	}
   589
kernel test robot Aug. 31, 2023, 1:28 p.m. UTC | #3
Hi Xabier,

kernel test robot noticed the following build errors:

[auto build test ERROR on net/main]
[also build test ERROR on net-next/main linus/master horms-ipvs/master v6.5 next-20230831]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xabier-Marquiegui/ptp-Demultiplexed-timestamp-channels/20230831-054428
base:   net/main
patch link:    https://lore.kernel.org/r/20230830214101.509086-2-reibax%40gmail.com
patch subject: [PATCH] ptp: Demultiplexed timestamp channels
config: powerpc64-randconfig-r026-20230831 (https://download.01.org/0day-ci/archive/20230831/202308312155.cA1uQaGm-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project.git 4a5ac14ee968ff0ad5d2cc1ffa0299048db4c88a)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230831/202308312155.cA1uQaGm-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202308312155.cA1uQaGm-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

>> drivers/ptp/ptp_chardev.c:449:9: warning: no previous prototype for function 'ptp_queue_read' [-Wmissing-prototypes]
     449 | ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
         |         ^
   drivers/ptp/ptp_chardev.c:449:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     449 | ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
         | ^
         | static 
>> drivers/ptp/ptp_chardev.c:544:5: warning: no previous prototype for function 'ptp_dmtsc_release' [-Wmissing-prototypes]
     544 | int ptp_dmtsc_release(struct inode *inode, struct file *file)
         |     ^
   drivers/ptp/ptp_chardev.c:544:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     544 | int ptp_dmtsc_release(struct inode *inode, struct file *file)
         | ^
         | static 
>> drivers/ptp/ptp_chardev.c:556:9: warning: no previous prototype for function 'ptp_dmtsc_read' [-Wmissing-prototypes]
     556 | ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
         |         ^
   drivers/ptp/ptp_chardev.c:556:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     556 | ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
         | ^
         | static 
>> drivers/ptp/ptp_chardev.c:571:6: warning: no previous prototype for function 'ptp_dmtsc_cdev_clean' [-Wmissing-prototypes]
     571 | void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
         |      ^
   drivers/ptp/ptp_chardev.c:571:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
     571 | void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
         | ^
         | static 
>> drivers/ptp/ptp_chardev.c:612:30: error: too many arguments to function call, expected single argument 'name', have 2 arguments
     612 |                         class_create(THIS_MODULE, "ptptsevqch_class");
         |                         ~~~~~~~~~~~~              ^~~~~~~~~~~~~~~~~~
   include/linux/device/class.h:230:29: note: 'class_create' declared here
     230 | struct class * __must_check class_create(const char *name);
         |                             ^
   4 warnings and 1 error generated.


vim +/name +612 drivers/ptp/ptp_chardev.c

   448	
 > 449	ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
   450			       int dmtsc)
   451	{
   452		struct timestamp_event_queue *queue;
   453		struct mutex *tsevq_mux;
   454		struct ptp_extts_event *event;
   455		unsigned long flags;
   456		size_t qcnt, i;
   457		int result;
   458	
   459		if (dmtsc < 0) {
   460			queue = &ptp->tsevq;
   461			tsevq_mux = &ptp->tsevq_mux;
   462		} else {
   463			queue = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq;
   464			tsevq_mux = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq_mux;
   465		}
   466	
   467		if (cnt % sizeof(struct ptp_extts_event) != 0)
   468			return -EINVAL;
   469	
   470		if (cnt > EXTTS_BUFSIZE)
   471			cnt = EXTTS_BUFSIZE;
   472	
   473		cnt = cnt / sizeof(struct ptp_extts_event);
   474	
   475		if (mutex_lock_interruptible(tsevq_mux))
   476			return -ERESTARTSYS;
   477	
   478		if (wait_event_interruptible(ptp->tsev_wq,
   479					     ptp->defunct || queue_cnt(queue))) {
   480			mutex_unlock(tsevq_mux);
   481			return -ERESTARTSYS;
   482		}
   483	
   484		if (ptp->defunct) {
   485			mutex_unlock(tsevq_mux);
   486			return -ENODEV;
   487		}
   488	
   489		event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
   490		if (!event) {
   491			mutex_unlock(tsevq_mux);
   492			return -ENOMEM;
   493		}
   494	
   495		spin_lock_irqsave(&queue->lock, flags);
   496	
   497		qcnt = queue_cnt(queue);
   498	
   499		if (cnt > qcnt)
   500			cnt = qcnt;
   501	
   502		for (i = 0; i < cnt; i++) {
   503			event[i] = queue->buf[queue->head];
   504			queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
   505		}
   506	
   507		spin_unlock_irqrestore(&queue->lock, flags);
   508	
   509		cnt = cnt * sizeof(struct ptp_extts_event);
   510	
   511		mutex_unlock(tsevq_mux);
   512	
   513		result = cnt;
   514		if (copy_to_user(buf, event, cnt))
   515			result = -EFAULT;
   516	
   517		kfree(event);
   518		return result;
   519	}
   520	
   521	ssize_t ptp_read(struct posix_clock *pc, uint rdflags, char __user *buf,
   522			 size_t cnt)
   523	{
   524		struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
   525	
   526		return ptp_queue_read(ptp, buf, cnt, DMTSC_NOT);
   527	}
   528	
   529	static int ptp_dmtsc_open(struct inode *inode, struct file *file)
   530	{
   531		struct ptp_dmtsc_cdev_info *cdev = container_of(
   532			inode->i_cdev, struct ptp_dmtsc_cdev_info, dmtsc_cdev);
   533	
   534		file->private_data = cdev;
   535	
   536		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   537			return -ERESTARTSYS;
   538		cdev->pclock->dmtsc_en_flags |= (0x1 << (cdev->minor));
   539		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   540	
   541		return stream_open(inode, file);
   542	}
   543	
 > 544	int ptp_dmtsc_release(struct inode *inode, struct file *file)
   545	{
   546		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   547	
   548		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   549			return -ERESTARTSYS;
   550		cdev->pclock->dmtsc_en_flags &= ~(0x1 << (cdev->minor));
   551		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   552	
   553		return 0;
   554	}
   555	
 > 556	ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
   557			       loff_t *offset)
   558	{
   559		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   560	
   561		return ptp_queue_read(cdev->pclock, buf, cnt, cdev->minor);
   562	}
   563	
   564	static const struct file_operations fops = {
   565							.owner = THIS_MODULE,
   566							.open = ptp_dmtsc_open,
   567							.read = ptp_dmtsc_read,
   568							.release = ptp_dmtsc_release
   569							};
   570	
 > 571	void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
   572	{
   573		int idx, major;
   574		dev_t device;
   575	
   576		major = MAJOR(ptp->dmtsc_devs.devid);
   577		for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
   578			if (ptp->dmtsc_devs.cdev_info[idx].minor >= 0) {
   579				device = MKDEV(major, idx);
   580				device_destroy(ptp->dmtsc_devs.dmtsc_class, device);
   581				cdev_del(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev);
   582				ptp->dmtsc_devs.cdev_info[idx].minor = -1;
   583			}
   584		}
   585		class_destroy(ptp->dmtsc_devs.dmtsc_class);
   586		unregister_chrdev_region(ptp->dmtsc_devs.devid, ptp->info->n_ext_ts);
   587		mutex_destroy(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
   588	}
   589	
   590	int ptp_dmtsc_dev_register(struct ptp_clock *ptp)
   591	{
   592		int err, idx, major;
   593		dev_t device;
   594		struct device *dev;
   595	
   596		/* Allocate memory for demuxed device management */
   597		ptp->dmtsc_devs.cdev_info = kcalloc(ptp->info->n_ext_ts,
   598						    sizeof(*ptp->dmtsc_devs.cdev_info),
   599						    GFP_KERNEL);
   600		if (!ptp->dmtsc_devs.cdev_info) {
   601			err = -ENODEV;
   602			goto err;
   603		}
   604		for (idx = 0; idx < ptp->info->n_ext_ts; idx++)
   605			ptp->dmtsc_devs.cdev_info[idx].minor = -1;
   606		/* Create devices for all channels. The mask will control which of them get fed */
   607		err = alloc_chrdev_region(&ptp->dmtsc_devs.devid, 0,
   608					  ptp->info->n_ext_ts, "ptptsevqch");
   609		if (!err) {
   610			major = MAJOR(ptp->dmtsc_devs.devid);
   611			ptp->dmtsc_devs.dmtsc_class =
 > 612				class_create(THIS_MODULE, "ptptsevqch_class");
   613			for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
   614				mutex_init(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
   615				device = MKDEV(major, idx);
   616				ptp->dmtsc_devs.cdev_info[idx].pclock = ptp;
   617				cdev_init(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev,
   618					  &fops);
   619				err = cdev_add(
   620					&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev,
   621					device, 1);
   622				if (err) {
   623					goto cdev_clean;
   624				} else {
   625					ptp->dmtsc_devs.cdev_info[idx].minor = idx;
   626					dev = device_create(ptp->dmtsc_devs.dmtsc_class,
   627							    &ptp->dev, device, NULL,
   628							    "ptp%dch%d", ptp->index,
   629							    idx);
   630					if (IS_ERR(dev)) {
   631						err = PTR_ERR(dev);
   632						goto cdev_clean;
   633					}
   634				}
   635			}
   636		} else {
   637			goto dev_clean;
   638		}
   639		return 0;
   640	
   641	cdev_clean:
   642		ptp_dmtsc_cdev_clean(ptp);
   643	dev_clean:
   644		kfree(ptp->dmtsc_devs.cdev_info);
   645		ptp->dmtsc_devs.cdev_info = NULL;
   646	err:
   647		return err;
   648	}
   649
kernel test robot Aug. 31, 2023, 4:20 p.m. UTC | #4
Hi Xabier,

kernel test robot noticed the following build warnings:

[auto build test WARNING on net/main]
[also build test WARNING on net-next/main linus/master horms-ipvs/master v6.5 next-20230831]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Xabier-Marquiegui/ptp-Demultiplexed-timestamp-channels/20230831-054428
base:   net/main
patch link:    https://lore.kernel.org/r/20230830214101.509086-2-reibax%40gmail.com
patch subject: [PATCH] ptp: Demultiplexed timestamp channels
config: um-i386_defconfig (https://download.01.org/0day-ci/archive/20230901/202309010030.U9g5VVWf-lkp@intel.com/config)
compiler: gcc-7 (Ubuntu 7.5.0-6ubuntu2) 7.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230901/202309010030.U9g5VVWf-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202309010030.U9g5VVWf-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/ptp/ptp_chardev.c:449:9: warning: no previous declaration for 'ptp_queue_read' [-Wmissing-declarations]
    ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
            ^~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:544:5: warning: no previous declaration for 'ptp_dmtsc_release' [-Wmissing-declarations]
    int ptp_dmtsc_release(struct inode *inode, struct file *file)
        ^~~~~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:556:9: warning: no previous declaration for 'ptp_dmtsc_read' [-Wmissing-declarations]
    ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
            ^~~~~~~~~~~~~~
>> drivers/ptp/ptp_chardev.c:571:6: warning: no previous declaration for 'ptp_dmtsc_cdev_clean' [-Wmissing-declarations]
    void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
         ^~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/linkage.h:7:0,
                    from include/linux/kernel.h:17,
                    from include/linux/cpumask.h:10,
                    from include/linux/mm_types_task.h:14,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/ptp/ptp_chardev.c:7:
   drivers/ptp/ptp_chardev.c: In function 'ptp_dmtsc_dev_register':
   include/linux/export.h:31:21: error: passing argument 1 of 'class_create' from incompatible pointer type [-Werror=incompatible-pointer-types]
    #define THIS_MODULE ((struct module *)0)
                        ^
   drivers/ptp/ptp_chardev.c:612:17: note: in expansion of macro 'THIS_MODULE'
       class_create(THIS_MODULE, "ptptsevqch_class");
                    ^~~~~~~~~~~
   In file included from include/linux/device.h:31:0,
                    from include/linux/cdev.h:8,
                    from include/linux/posix-clock.h:10,
                    from drivers/ptp/ptp_chardev.c:8:
   include/linux/device/class.h:230:29: note: expected 'const char *' but argument is of type 'struct module *'
    struct class * __must_check class_create(const char *name);
                                ^~~~~~~~~~~~
   drivers/ptp/ptp_chardev.c:612:4: error: too many arguments to function 'class_create'
       class_create(THIS_MODULE, "ptptsevqch_class");
       ^~~~~~~~~~~~
   In file included from include/linux/device.h:31:0,
                    from include/linux/cdev.h:8,
                    from include/linux/posix-clock.h:10,
                    from drivers/ptp/ptp_chardev.c:8:
   include/linux/device/class.h:230:29: note: declared here
    struct class * __must_check class_create(const char *name);
                                ^~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/ptp_queue_read +449 drivers/ptp/ptp_chardev.c

   448	
 > 449	ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
   450			       int dmtsc)
   451	{
   452		struct timestamp_event_queue *queue;
   453		struct mutex *tsevq_mux;
   454		struct ptp_extts_event *event;
   455		unsigned long flags;
   456		size_t qcnt, i;
   457		int result;
   458	
   459		if (dmtsc < 0) {
   460			queue = &ptp->tsevq;
   461			tsevq_mux = &ptp->tsevq_mux;
   462		} else {
   463			queue = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq;
   464			tsevq_mux = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq_mux;
   465		}
   466	
   467		if (cnt % sizeof(struct ptp_extts_event) != 0)
   468			return -EINVAL;
   469	
   470		if (cnt > EXTTS_BUFSIZE)
   471			cnt = EXTTS_BUFSIZE;
   472	
   473		cnt = cnt / sizeof(struct ptp_extts_event);
   474	
   475		if (mutex_lock_interruptible(tsevq_mux))
   476			return -ERESTARTSYS;
   477	
   478		if (wait_event_interruptible(ptp->tsev_wq,
   479					     ptp->defunct || queue_cnt(queue))) {
   480			mutex_unlock(tsevq_mux);
   481			return -ERESTARTSYS;
   482		}
   483	
   484		if (ptp->defunct) {
   485			mutex_unlock(tsevq_mux);
   486			return -ENODEV;
   487		}
   488	
   489		event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
   490		if (!event) {
   491			mutex_unlock(tsevq_mux);
   492			return -ENOMEM;
   493		}
   494	
   495		spin_lock_irqsave(&queue->lock, flags);
   496	
   497		qcnt = queue_cnt(queue);
   498	
   499		if (cnt > qcnt)
   500			cnt = qcnt;
   501	
   502		for (i = 0; i < cnt; i++) {
   503			event[i] = queue->buf[queue->head];
   504			queue->head = (queue->head + 1) % PTP_MAX_TIMESTAMPS;
   505		}
   506	
   507		spin_unlock_irqrestore(&queue->lock, flags);
   508	
   509		cnt = cnt * sizeof(struct ptp_extts_event);
   510	
   511		mutex_unlock(tsevq_mux);
   512	
   513		result = cnt;
   514		if (copy_to_user(buf, event, cnt))
   515			result = -EFAULT;
   516	
   517		kfree(event);
   518		return result;
   519	}
   520	
   521	ssize_t ptp_read(struct posix_clock *pc, uint rdflags, char __user *buf,
   522			 size_t cnt)
   523	{
   524		struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
   525	
   526		return ptp_queue_read(ptp, buf, cnt, DMTSC_NOT);
   527	}
   528	
   529	static int ptp_dmtsc_open(struct inode *inode, struct file *file)
   530	{
   531		struct ptp_dmtsc_cdev_info *cdev = container_of(
   532			inode->i_cdev, struct ptp_dmtsc_cdev_info, dmtsc_cdev);
   533	
   534		file->private_data = cdev;
   535	
   536		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   537			return -ERESTARTSYS;
   538		cdev->pclock->dmtsc_en_flags |= (0x1 << (cdev->minor));
   539		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   540	
   541		return stream_open(inode, file);
   542	}
   543	
 > 544	int ptp_dmtsc_release(struct inode *inode, struct file *file)
   545	{
   546		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   547	
   548		if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
   549			return -ERESTARTSYS;
   550		cdev->pclock->dmtsc_en_flags &= ~(0x1 << (cdev->minor));
   551		mutex_unlock(&cdev->pclock->dmtsc_en_mux);
   552	
   553		return 0;
   554	}
   555	
 > 556	ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
   557			       loff_t *offset)
   558	{
   559		struct ptp_dmtsc_cdev_info *cdev = file->private_data;
   560	
   561		return ptp_queue_read(cdev->pclock, buf, cnt, cdev->minor);
   562	}
   563	
   564	static const struct file_operations fops = {
   565							.owner = THIS_MODULE,
   566							.open = ptp_dmtsc_open,
   567							.read = ptp_dmtsc_read,
   568							.release = ptp_dmtsc_release
   569							};
   570	
 > 571	void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
   572	{
   573		int idx, major;
   574		dev_t device;
   575	
   576		major = MAJOR(ptp->dmtsc_devs.devid);
   577		for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
   578			if (ptp->dmtsc_devs.cdev_info[idx].minor >= 0) {
   579				device = MKDEV(major, idx);
   580				device_destroy(ptp->dmtsc_devs.dmtsc_class, device);
   581				cdev_del(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev);
   582				ptp->dmtsc_devs.cdev_info[idx].minor = -1;
   583			}
   584		}
   585		class_destroy(ptp->dmtsc_devs.dmtsc_class);
   586		unregister_chrdev_region(ptp->dmtsc_devs.devid, ptp->info->n_ext_ts);
   587		mutex_destroy(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
   588	}
   589
Xabier Marquiegui Sept. 6, 2023, 10:47 a.m. UTC | #5
Thank you for your patience Richard, and thank you for guiding me. Let's
see if I got closer to what we want this time.

Let me know what you thing about these patches, please.

Thanks,
Xabier
diff mbox series

Patch

diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 362bf756e6b7..c31cfc5b0907 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -10,11 +10,14 @@ 
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/timekeeping.h>
-
+#include <linux/cdev.h>
+#include <linux/fs.h>
 #include <linux/nospec.h>
 
 #include "ptp_private.h"
 
+#define DMTSC_NOT -1
+
 static int ptp_disable_pinfunc(struct ptp_clock_info *ops,
 			       enum ptp_pin_function func, unsigned int chan)
 {
@@ -443,16 +446,24 @@  __poll_t ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait)
 
 #define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event))
 
-ssize_t ptp_read(struct posix_clock *pc,
-		 uint rdflags, char __user *buf, size_t cnt)
+ssize_t ptp_queue_read(struct ptp_clock *ptp, char __user *buf, size_t cnt,
+		       int dmtsc)
 {
-	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
-	struct timestamp_event_queue *queue = &ptp->tsevq;
+	struct timestamp_event_queue *queue;
+	struct mutex *tsevq_mux;
 	struct ptp_extts_event *event;
 	unsigned long flags;
 	size_t qcnt, i;
 	int result;
 
+	if (dmtsc < 0) {
+		queue = &ptp->tsevq;
+		tsevq_mux = &ptp->tsevq_mux;
+	} else {
+		queue = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq;
+		tsevq_mux = &ptp->dmtsc_devs.cdev_info[dmtsc].tsevq_mux;
+	}
+
 	if (cnt % sizeof(struct ptp_extts_event) != 0)
 		return -EINVAL;
 
@@ -461,23 +472,23 @@  ssize_t ptp_read(struct posix_clock *pc,
 
 	cnt = cnt / sizeof(struct ptp_extts_event);
 
-	if (mutex_lock_interruptible(&ptp->tsevq_mux))
+	if (mutex_lock_interruptible(tsevq_mux))
 		return -ERESTARTSYS;
 
 	if (wait_event_interruptible(ptp->tsev_wq,
 				     ptp->defunct || queue_cnt(queue))) {
-		mutex_unlock(&ptp->tsevq_mux);
+		mutex_unlock(tsevq_mux);
 		return -ERESTARTSYS;
 	}
 
 	if (ptp->defunct) {
-		mutex_unlock(&ptp->tsevq_mux);
+		mutex_unlock(tsevq_mux);
 		return -ENODEV;
 	}
 
 	event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL);
 	if (!event) {
-		mutex_unlock(&ptp->tsevq_mux);
+		mutex_unlock(tsevq_mux);
 		return -ENOMEM;
 	}
 
@@ -497,7 +508,7 @@  ssize_t ptp_read(struct posix_clock *pc,
 
 	cnt = cnt * sizeof(struct ptp_extts_event);
 
-	mutex_unlock(&ptp->tsevq_mux);
+	mutex_unlock(tsevq_mux);
 
 	result = cnt;
 	if (copy_to_user(buf, event, cnt))
@@ -506,3 +517,139 @@  ssize_t ptp_read(struct posix_clock *pc,
 	kfree(event);
 	return result;
 }
+
+ssize_t ptp_read(struct posix_clock *pc, uint rdflags, char __user *buf,
+		 size_t cnt)
+{
+	struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock);
+
+	return ptp_queue_read(ptp, buf, cnt, DMTSC_NOT);
+}
+
+static int ptp_dmtsc_open(struct inode *inode, struct file *file)
+{
+	struct ptp_dmtsc_cdev_info *cdev = container_of(
+		inode->i_cdev, struct ptp_dmtsc_cdev_info, dmtsc_cdev);
+
+	file->private_data = cdev;
+
+	if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
+		return -ERESTARTSYS;
+	cdev->pclock->dmtsc_en_flags |= (0x1 << (cdev->minor));
+	mutex_unlock(&cdev->pclock->dmtsc_en_mux);
+
+	return stream_open(inode, file);
+}
+
+int ptp_dmtsc_release(struct inode *inode, struct file *file)
+{
+	struct ptp_dmtsc_cdev_info *cdev = file->private_data;
+
+	if (mutex_lock_interruptible(&cdev->pclock->dmtsc_en_mux))
+		return -ERESTARTSYS;
+	cdev->pclock->dmtsc_en_flags &= ~(0x1 << (cdev->minor));
+	mutex_unlock(&cdev->pclock->dmtsc_en_mux);
+
+	return 0;
+}
+
+ssize_t ptp_dmtsc_read(struct file *file, char __user *buf, size_t cnt,
+		       loff_t *offset)
+{
+	struct ptp_dmtsc_cdev_info *cdev = file->private_data;
+
+	return ptp_queue_read(cdev->pclock, buf, cnt, cdev->minor);
+}
+
+static const struct file_operations fops = {
+						.owner = THIS_MODULE,
+						.open = ptp_dmtsc_open,
+						.read = ptp_dmtsc_read,
+						.release = ptp_dmtsc_release
+						};
+
+void ptp_dmtsc_cdev_clean(struct ptp_clock *ptp)
+{
+	int idx, major;
+	dev_t device;
+
+	major = MAJOR(ptp->dmtsc_devs.devid);
+	for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
+		if (ptp->dmtsc_devs.cdev_info[idx].minor >= 0) {
+			device = MKDEV(major, idx);
+			device_destroy(ptp->dmtsc_devs.dmtsc_class, device);
+			cdev_del(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev);
+			ptp->dmtsc_devs.cdev_info[idx].minor = -1;
+		}
+	}
+	class_destroy(ptp->dmtsc_devs.dmtsc_class);
+	unregister_chrdev_region(ptp->dmtsc_devs.devid, ptp->info->n_ext_ts);
+	mutex_destroy(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
+}
+
+int ptp_dmtsc_dev_register(struct ptp_clock *ptp)
+{
+	int err, idx, major;
+	dev_t device;
+	struct device *dev;
+
+	/* Allocate memory for demuxed device management */
+	ptp->dmtsc_devs.cdev_info = kcalloc(ptp->info->n_ext_ts,
+					    sizeof(*ptp->dmtsc_devs.cdev_info),
+					    GFP_KERNEL);
+	if (!ptp->dmtsc_devs.cdev_info) {
+		err = -ENODEV;
+		goto err;
+	}
+	for (idx = 0; idx < ptp->info->n_ext_ts; idx++)
+		ptp->dmtsc_devs.cdev_info[idx].minor = -1;
+	/* Create devices for all channels. The mask will control which of them get fed */
+	err = alloc_chrdev_region(&ptp->dmtsc_devs.devid, 0,
+				  ptp->info->n_ext_ts, "ptptsevqch");
+	if (!err) {
+		major = MAJOR(ptp->dmtsc_devs.devid);
+		ptp->dmtsc_devs.dmtsc_class =
+			class_create(THIS_MODULE, "ptptsevqch_class");
+		for (idx = 0; idx < ptp->info->n_ext_ts; idx++) {
+			mutex_init(&ptp->dmtsc_devs.cdev_info[idx].tsevq_mux);
+			device = MKDEV(major, idx);
+			ptp->dmtsc_devs.cdev_info[idx].pclock = ptp;
+			cdev_init(&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev,
+				  &fops);
+			err = cdev_add(
+				&ptp->dmtsc_devs.cdev_info[idx].dmtsc_cdev,
+				device, 1);
+			if (err) {
+				goto cdev_clean;
+			} else {
+				ptp->dmtsc_devs.cdev_info[idx].minor = idx;
+				dev = device_create(ptp->dmtsc_devs.dmtsc_class,
+						    &ptp->dev, device, NULL,
+						    "ptp%dch%d", ptp->index,
+						    idx);
+				if (IS_ERR(dev)) {
+					err = PTR_ERR(dev);
+					goto cdev_clean;
+				}
+			}
+		}
+	} else {
+		goto dev_clean;
+	}
+	return 0;
+
+cdev_clean:
+	ptp_dmtsc_cdev_clean(ptp);
+dev_clean:
+	kfree(ptp->dmtsc_devs.cdev_info);
+	ptp->dmtsc_devs.cdev_info = NULL;
+err:
+	return err;
+}
+
+void ptp_dmtsc_dev_uregister(struct ptp_clock *ptp)
+{
+	ptp_dmtsc_cdev_clean(ptp);
+	kfree(ptp->dmtsc_devs.cdev_info);
+	ptp->dmtsc_devs.cdev_info = NULL;
+}
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index 80f74e38c2da..0a42c27c3514 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -172,6 +172,7 @@  static void ptp_clock_release(struct device *dev)
 
 	ptp_cleanup_pin_groups(ptp);
 	kfree(ptp->vclock_index);
+	mutex_destroy(&ptp->dmtsc_en_mux);
 	mutex_destroy(&ptp->tsevq_mux);
 	mutex_destroy(&ptp->pincfg_mux);
 	mutex_destroy(&ptp->n_vclocks_mux);
@@ -232,7 +233,9 @@  struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 	mutex_init(&ptp->tsevq_mux);
 	mutex_init(&ptp->pincfg_mux);
 	mutex_init(&ptp->n_vclocks_mux);
+	mutex_init(&ptp->dmtsc_en_mux);
 	init_waitqueue_head(&ptp->tsev_wq);
+	ptp->dmtsc_en_flags = 0x0;
 
 	if (ptp->info->getcycles64 || ptp->info->getcyclesx64) {
 		ptp->has_cycles = true;
@@ -307,21 +310,27 @@  struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 
 	/* Create a posix clock and link it to the device. */
 	err = posix_clock_register(&ptp->clock, &ptp->dev);
-	if (err) {
-		if (ptp->pps_source)
-			pps_unregister_source(ptp->pps_source);
+	if (err)
+		goto reg_err;
 
-		if (ptp->kworker)
-			kthread_destroy_worker(ptp->kworker);
+	/* Create chardevs for demuxed external timestamp channels */
+	if (ptp_dmtsc_dev_register(ptp))
+		goto reg_err;
 
-		put_device(&ptp->dev);
+	return ptp;
 
-		pr_err("failed to create posix clock\n");
-		return ERR_PTR(err);
-	}
+reg_err:
+	ptp_dmtsc_dev_uregister(ptp);
+	if (ptp->pps_source)
+		pps_unregister_source(ptp->pps_source);
 
-	return ptp;
+	if (ptp->kworker)
+		kthread_destroy_worker(ptp->kworker);
+
+	put_device(&ptp->dev);
 
+	pr_err("failed to create posix clock\n");
+	return ERR_PTR(err);
 no_pps:
 	ptp_cleanup_pin_groups(ptp);
 no_pin_groups:
@@ -330,6 +339,7 @@  struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
 	if (ptp->kworker)
 		kthread_destroy_worker(ptp->kworker);
 kworker_err:
+	mutex_destroy(&ptp->dmtsc_en_mux);
 	mutex_destroy(&ptp->tsevq_mux);
 	mutex_destroy(&ptp->pincfg_mux);
 	mutex_destroy(&ptp->n_vclocks_mux);
@@ -367,6 +377,8 @@  int ptp_clock_unregister(struct ptp_clock *ptp)
 	if (ptp->pps_source)
 		pps_unregister_source(ptp->pps_source);
 
+	ptp_dmtsc_dev_uregister(ptp);
+
 	posix_clock_unregister(&ptp->clock);
 
 	return 0;
@@ -378,12 +390,19 @@  void ptp_clock_event(struct ptp_clock *ptp, struct ptp_clock_event *event)
 	struct pps_event_time evt;
 
 	switch (event->type) {
-
 	case PTP_CLOCK_ALARM:
 		break;
 
 	case PTP_CLOCK_EXTTS:
-		enqueue_external_timestamp(&ptp->tsevq, event);
+		/* If event index demuxed queue mask is enabled send to dedicated fifo */
+		if (ptp->dmtsc_en_flags & (0x1 << event->index)) {
+			enqueue_external_timestamp(
+				&ptp->dmtsc_devs.cdev_info[event->index].tsevq,
+				event);
+		} else {
+			enqueue_external_timestamp(&ptp->tsevq, event);
+		}
+
 		wake_up_interruptible(&ptp->tsev_wq);
 		break;
 
diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h
index 75f58fc468a7..c473ef75d8d7 100644
--- a/drivers/ptp/ptp_private.h
+++ b/drivers/ptp/ptp_private.h
@@ -27,6 +27,20 @@  struct timestamp_event_queue {
 	spinlock_t lock;
 };
 
+struct ptp_dmtsc_cdev_info {
+	struct cdev dmtsc_cdev; /* Demuxed event device chardev */
+	int minor; /* Demuxed event queue chardev device minor */
+	struct ptp_clock *pclock; /* Direct access to parent clock device */
+	struct mutex tsevq_mux; /* Protect access to device management */
+	struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+};
+
+struct ptp_dmtsc_dev_info {
+	dev_t devid;
+	struct class *dmtsc_class;
+	struct ptp_dmtsc_cdev_info *cdev_info;
+};
+
 struct ptp_clock {
 	struct posix_clock clock;
 	struct device dev;
@@ -36,6 +50,11 @@  struct ptp_clock {
 	struct pps_device *pps_source;
 	long dialed_frequency; /* remembers the frequency adjustment */
 	struct timestamp_event_queue tsevq; /* simple fifo for time stamps */
+	u32 dmtsc_en_flags; /* Demultiplexed timestamp channels enable flags */
+	struct mutex
+		dmtsc_en_mux; /* Demultiplexed timestamp channels sysfs mutex */
+	struct ptp_dmtsc_dev_info
+		dmtsc_devs; /* Demultiplexed timestamp channel access character devices */
 	struct mutex tsevq_mux; /* one process at a time reading the fifo */
 	struct mutex pincfg_mux; /* protect concurrent info->pin_config access */
 	wait_queue_head_t tsev_wq;
@@ -139,4 +158,7 @@  void ptp_cleanup_pin_groups(struct ptp_clock *ptp);
 
 struct ptp_vclock *ptp_vclock_register(struct ptp_clock *pclock);
 void ptp_vclock_unregister(struct ptp_vclock *vclock);
+
+int ptp_dmtsc_dev_register(struct ptp_clock *ptp);
+void ptp_dmtsc_dev_uregister(struct ptp_clock *ptp);
 #endif