Hi Greg. > -----Original Message----- > From: Greg KH [mailto:gregkh@xxxxxxxxxxxxxxxxxxx] > Sent: Friday, October 20, 2017 2:55 PM > To: Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx> > Cc: arnd@xxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; linux-arm- > kernel@xxxxxxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; > openbmc@xxxxxxxxxxxxxxxx; joel@xxxxxxxxx; jiri@xxxxxxxxxxx; > tklauser@xxxxxxxxxx; linux-serial@xxxxxxxxxxxxxxx; mec@xxxxxxxxx; Vadim > Pasternak <vadimp@xxxxxxxxxxxx>; system-sw-low-level <system-sw-low- > level@xxxxxxxxxxxx>; robh+dt@xxxxxxxxxx; openocd-devel- > owner@xxxxxxxxxxxxxxxxxxxxx; linux-api@xxxxxxxxxxxxxxx; > davem@xxxxxxxxxxxxx; mchehab@xxxxxxxxxx; Jiri Pirko <jiri@xxxxxxxxxxxx> > Subject: Re: [patch v9 1/4] drivers: jtag: Add JTAG core driver > > On Thu, Sep 21, 2017 at 12:25:29PM +0300, Oleksandr Shamray wrote: > > +struct jtag { > > + struct device *dev; > > + struct cdev cdev; > > Why are you using a cdev here and not just a normal misc device? What the benefits to use misc instead of cdev? > I forgot if this is what you were doing before, sorry... > > > + int id; > > + atomic_t open; > > Why do you need this? This counter used to avoid open at the same time by 2 or more users. > > > + const struct jtag_ops *ops; > > + unsigned long priv[0] __aligned(ARCH_DMA_MINALIGN); > > Huh? Why is this needed to be dma aligned? Why not just use the private > pointer in struct device? > It is critical? > > +}; > > + > > +static dev_t jtag_devt; > > +static DEFINE_IDA(jtag_ida); > > + > > +void *jtag_priv(struct jtag *jtag) > > +{ > > + return jtag->priv; > > +} > > +EXPORT_SYMBOL_GPL(jtag_priv); > > + > > +static u8 *jtag_copy_from_user(__u64 udata, unsigned long bit_size) { > > + unsigned long size; > > + void *kdata; > > + > > + size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE); > > + kdata = memdup_user(u64_to_user_ptr(udata), size); > > You only use this once, why not just open-code it? I think it makes code more understandable. > > > + > > + return kdata; > > +} > > + > > +static unsigned long jtag_copy_to_user(__u64 udata, u8 *kdata, > > + unsigned long bit_size) > > +{ > > + unsigned long size; > > + > > + size = DIV_ROUND_UP(bit_size, BITS_PER_BYTE); > > + > > + return copy_to_user(u64_to_user_ptr(udata), (void *)(kdata), size); > > Same here, making this a separate function seems odd. Same, I think it makes code more understandable. > > > +} > > + > > +static struct class jtag_class = { > > + .name = "jtag", > > + .owner = THIS_MODULE, > > +}; > > + > > +static int jtag_run_test_idle_op(struct jtag *jtag, > > + struct jtag_run_test_idle *idle) { > > + if (jtag->ops->idle) > > + return jtag->ops->idle(jtag, idle); > > + else > > + return -EOPNOTSUPP; > > Why is this a function? Why not just do this work in the ioctl? Agree. I will move it just to ioctl function body. > > > +} > > + > > +static int jtag_xfer_op(struct jtag *jtag, struct jtag_xfer *xfer, u8 > > +*data) { > > + if (jtag->ops->xfer) > > + return jtag->ops->xfer(jtag, xfer, data); > > + else > > + return -EOPNOTSUPP; > > Same here, doesn't need to be a function. Agree. > > > +} > > + > > +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned > > +long arg) { > > + struct jtag *jtag = file->private_data; > > + struct jtag_run_test_idle idle; > > + struct jtag_xfer xfer; > > + u8 *xfer_data; > > + u32 value; > > + int err; > > + > > + switch (cmd) { > > + case JTAG_GIOCFREQ: > > + if (jtag->ops->freq_get) > > + err = jtag->ops->freq_get(jtag, &value); > > + else > > + err = -EOPNOTSUPP; > > + if (err) > > + break; > > + > > + err = put_user(value, (__u32 *)arg); > > + break; > > Are you sure the return value of put_user is correct here? Shouldn't you return > -EFAULT if err is not 0? Yes, thanks for point of this. > > > + > > + case JTAG_SIOCFREQ: > > + err = get_user(value, (__u32 *)arg); > > + > > why a blank line? Will remove > > > + if (value == 0) > > + err = -EINVAL; > > Check err first. Ok. > > > + if (err) > > + break; > > And set it properly, again err should be -EFAULT, right? Right 😊0 > > > + > > + if (jtag->ops->freq_set) > > + err = jtag->ops->freq_set(jtag, value); > > + else > > + err = -EOPNOTSUPP; > > + break; > > + > > + case JTAG_IOCRUNTEST: > > + if (copy_from_user(&idle, (void *)arg, > > + sizeof(struct jtag_run_test_idle))) > > + return -ENOMEM; > > + err = jtag_run_test_idle_op(jtag, &idle); > > Who validates the structure fields? Is that up to the individual jtag driver? Why > not do it in the core correctly so that it only has to be done in one place and you > do not have to audit every individual driver? Input parameters validated by jtag platform driver. I think it not critical. > > > + break; > > + > > + case JTAG_IOCXFER: > > + if (copy_from_user(&xfer, (void *)arg, > > + sizeof(struct jtag_xfer))) > > + return -EFAULT; > > + > > + if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) > > + return -EFAULT; > > + > > + xfer_data = jtag_copy_from_user(xfer.tdio, xfer.length); > > + if (!xfer_data) > > + return -ENOMEM; > > Are you sure that's the correct error value? I think yes, but what you suggest? > > > + > > + err = jtag_xfer_op(jtag, &xfer, xfer_data); > > + if (jtag_copy_to_user(xfer.tdio, xfer_data, xfer.length)) { > > + kfree(xfer_data); > > + return -EFAULT; > > + } > > + > > + kfree(xfer_data); > > + if (copy_to_user((void *)arg, &xfer, sizeof(struct jtag_xfer))) > > + return -EFAULT; > > + break; > > + > > + case JTAG_GIOCSTATUS: > > + if (jtag->ops->status_get) > > + err = jtag->ops->status_get(jtag, &value); > > + else > > + err = -EOPNOTSUPP; > > + if (err) > > + break; > > + > > + err = put_user(value, (__u32 *)arg); > > Same put_user err issue here. Yes. > > > + break; > > + > > + default: > > + return -EINVAL; > > + } > > + return err; > > +} > > + > > +#ifdef CONFIG_COMPAT > > +static long jtag_ioctl_compat(struct file *file, unsigned int cmd, > > + unsigned long arg) > > +{ > > + return jtag_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } > > +#endif > > Why do you need a compat callback for a new ioctl? Create your structures > properly and this should not be needed, right? I does this because Arnd (arndbergmann@xxxxxxxxx) writed in review (On Wed, Aug 2, 2017 at 3:18 PM) [..] > + .unlocked_ioctl = jtag_ioctl, > + .open = jtag_open, > + .release = jtag_release, > +}; add a compat_ioctl pointer here, after ensuring that all ioctl commands are compatible between 32-bit and 64-bit user space. [..] > > > + > > +static int jtag_open(struct inode *inode, struct file *file) { > > + struct jtag *jtag = container_of(inode->i_cdev, struct jtag, cdev); > > + > > + if (atomic_read(&jtag->open)) { > > + dev_info(NULL, "jtag already opened\n"); > > + return -EBUSY; > > Why do you care if multiple opens can happen? Jtag HW not support to using with multiple requests from different users. So we prohibit this. > > > + } > > + > > + atomic_inc(&jtag->open); > > Oh look, you just raced with two different people opening at the same time :( > > An atomic value is never a replacement for a lock, sorry. > > > + file->private_data = jtag; > > + return 0; > > +} > > + > > +static int jtag_release(struct inode *inode, struct file *file) { > > + struct jtag *jtag = file->private_data; > > + > > + atomic_dec(&jtag->open); > > Again, not needed. > > > + return 0; > > +} > > + > > +static const struct file_operations jtag_fops = { > > + .owner = THIS_MODULE, > > + .open = jtag_open, > > + .release = jtag_release, > > + .llseek = noop_llseek, > > + .unlocked_ioctl = jtag_ioctl, > > +#ifdef CONFIG_COMPAT > > + .compat_ioctl = jtag_ioctl_compat, > > +#endif > > +}; > > + > > +struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops) > > +{ > > + struct jtag *jtag; > > + > > + jtag = kzalloc(sizeof(*jtag) + round_up(priv_size, > ARCH_DMA_MINALIGN), > > + GFP_KERNEL); > > + if (!jtag) > > + return NULL; > > + > > + jtag->ops = ops; > > + return jtag; > > +} > > +EXPORT_SYMBOL_GPL(jtag_alloc); > > register should allocate and register the device, why pass a structure back that > the caller then has to do something else with to use it? > Before registration we need to initialize JTAG device HW registers and fill private data with HW specific parameters. And is I see in other Linux drivers it is a common way to separate allocation device and register it > > + > > +void jtag_free(struct jtag *jtag) > > +{ > > + kfree(jtag); > > +} > > +EXPORT_SYMBOL_GPL(jtag_free); > > + > > +int jtag_register(struct jtag *jtag) > > +{ > > + int id; > > + int err; > > + > > + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); > > + if (id < 0) > > + return id; > > + > > + jtag->id = id; > > + cdev_init(&jtag->cdev, &jtag_fops); > > + jtag->cdev.owner = THIS_MODULE; > > + err = cdev_add(&jtag->cdev, MKDEV(MAJOR(jtag_devt), jtag->id), 1); > > + if (err) > > + goto err_cdev; > > misc device would be so much simpler and easier here :( > > > > + > > + /* Register this jtag device with the driver core */ > > + jtag->dev = device_create(&jtag_class, NULL, > MKDEV(MAJOR(jtag_devt), > > + jtag->id), > > + NULL, "jtag%d", jtag->id); > > + if (!jtag->dev) > > + goto err_device_create; > > + > > + atomic_set(&jtag->open, 0); > > + dev_set_drvdata(jtag->dev, jtag); > > + return err; > > + > > +err_device_create: > > + cdev_del(&jtag->cdev); > > +err_cdev: > > + ida_simple_remove(&jtag_ida, id); > > + return err; > > +} > > +EXPORT_SYMBOL_GPL(jtag_register); > > + > > +void jtag_unregister(struct jtag *jtag) { > > + struct device *dev = jtag->dev; > > + > > + cdev_del(&jtag->cdev); > > + device_unregister(dev); > > + ida_simple_remove(&jtag_ida, jtag->id); } > > +EXPORT_SYMBOL_GPL(jtag_unregister); > > + > > +static int __init jtag_init(void) > > +{ > > + int err; > > + > > + err = alloc_chrdev_region(&jtag_devt, JTAG_FIRST_MINOR, > > + JTAG_MAX_DEVICES, "jtag"); > > + if (err) > > + return err; > > + > > + err = class_register(&jtag_class); > > + if (err) > > + unregister_chrdev_region(jtag_devt, JTAG_MAX_DEVICES); > > + > > + return err; > > +} > > + > > +static void __exit jtag_exit(void) > > +{ > > + class_unregister(&jtag_class); > > + unregister_chrdev_region(jtag_devt, JTAG_MAX_DEVICES); > > Your idr leaked memory :( > Yes I will add ida_destroy() here. > > > > +} > > + > > +module_init(jtag_init); > > +module_exit(jtag_exit); > > + > > +MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx>"); > > +MODULE_DESCRIPTION("Generic jtag support"); MODULE_LICENSE("GPL > v2"); > > diff --git a/include/linux/jtag.h b/include/linux/jtag.h new file mode > > 100644 index 0000000..c016fed > > --- /dev/null > > +++ b/include/linux/jtag.h > > @@ -0,0 +1,48 @@ > > +/* > > + * drivers/jtag/jtag.c > > + * > > + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. > > + * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx> > > + * > > + * Released under the GPLv2 only. > > + * SPDX-License-Identifier: GPL-2.0 > > + */ > > + > > +#ifndef __JTAG_H > > +#define __JTAG_H > > + > > +#include <uapi/linux/jtag.h> > > + > > +#ifndef ARCH_DMA_MINALIGN > > +#define ARCH_DMA_MINALIGN 1 > > +#endif > > + > > +#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg) > > + > > +#define JTAG_MAX_XFER_DATA_LEN 65535 > > + > > +struct jtag; > > +/** > > + * struct jtag_ops - callbacks for jtag control functions: > > + * > > + * @freq_get: get frequency function. Filled by device driver > > + * @freq_set: set frequency function. Filled by device driver > > + * @status_get: set status function. Filled by device driver > > + * @idle: set JTAG to idle state function. Filled by device driver > > + * @xfer: send JTAG xfer function. Filled by device driver */ struct > > +jtag_ops { > > + int (*freq_get)(struct jtag *jtag, u32 *freq); > > + int (*freq_set)(struct jtag *jtag, u32 freq); > > + int (*status_get)(struct jtag *jtag, u32 *state); > > + int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle); > > + int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer); }; > > + > > +void *jtag_priv(struct jtag *jtag); > > +int jtag_register(struct jtag *jtag); void jtag_unregister(struct > > +jtag *jtag); struct jtag *jtag_alloc(size_t priv_size, const struct > > +jtag_ops *ops); void jtag_free(struct jtag *jtag); > > + > > +#endif /* __JTAG_H */ > > diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h new > > file mode 100644 index 0000000..180bf22 > > --- /dev/null > > +++ b/include/uapi/linux/jtag.h > > @@ -0,0 +1,115 @@ > > +/* > > + * JTAG class driver > > + * > > + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. > > + * Copyright (c) 2017 Oleksandr Shamray <oleksandrs@xxxxxxxxxxxx> > > + * > > + * Released under the GPLv2/BSD. > > + * SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ > > + > > +#ifndef __UAPI_LINUX_JTAG_H > > +#define __UAPI_LINUX_JTAG_H > > + > > +/** > > + * enum jtag_xfer_mode: > > + * > > + * @JTAG_XFER_HW_MODE: hardware mode transfer > > + * @JTAG_XFER_SW_MODE: software mode transfer */ enum > jtag_xfer_mode > > +{ > > + JTAG_XFER_HW_MODE, > > + JTAG_XFER_SW_MODE, > > +}; > > + > > +/** > > + * enum jtag_endstate: > > + * > > + * @JTAG_STATE_IDLE: JTAG state machine IDLE state > > + * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state > > + * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state */ enum > > +jtag_endstate { > > + JTAG_STATE_IDLE, > > + JTAG_STATE_PAUSEIR, > > + JTAG_STATE_PAUSEDR, > > +}; > > + > > +/** > > + * enum jtag_xfer_type: > > + * > > + * @JTAG_SIR_XFER: SIR transfer > > + * @JTAG_SDR_XFER: SDR transfer > > + */ > > +enum jtag_xfer_type { > > + JTAG_SIR_XFER, > > + JTAG_SDR_XFER, > > +}; > > + > > +/** > > + * enum jtag_xfer_direction: > > + * > > + * @JTAG_READ_XFER: read transfer > > + * @JTAG_WRITE_XFER: write transfer > > + */ > > +enum jtag_xfer_direction { > > + JTAG_READ_XFER, > > + JTAG_WRITE_XFER, > > +}; > > + > > +/** > > + * struct jtag_run_test_idle - forces JTAG state machine to > > + * RUN_TEST/IDLE state > > + * > > + * @mode: access mode > > + * @reset: 0 - run IDLE/PAUSE from current state > > + * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE > > + * @end: completion flag > > + * @tck: clock counter > > + * > > + * Structure represents interface to JTAG device for jtag idle > > + * execution. > > + */ > > +struct jtag_run_test_idle { > > + __u8 mode; > > + __u8 reset; > > + __u8 endstate; > > + __u8 tck; > > +}; > > + > > +/** > > + * struct jtag_xfer - jtag xfer: > > + * > > + * @mode: access mode > > + * @type: transfer type > > + * @direction: xfer direction > > + * @length: xfer bits len > > + * @tdio : xfer data array > > + * @endir: xfer end state > > + * > > + * Structure represents interface to Aspeed JTAG device for jtag sdr > > +xfer > > + * execution. > > + */ > > +struct jtag_xfer { > > + __u8 mode; > > + __u8 type; > > + __u8 direction; > > + __u8 endstate; > > + __u32 length; > > + __u64 tdio; > > I don't see anything odd here that requires a compat ioctl callback, do you? > > thanks, > > greg k-h Thanks for your review. Oleksandr S. ��.n��������+%������w��{.n����z�{��ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f