/* * Freescale QUICC Engine USB Host Controller Driver * * Copyright (c) Freescale Semicondutor, Inc. 2006. * Shlomi Gridish * Jerry Huang * Copyright (c) Logic Product Development, Inc. 2007 * Peter Barada * Copyright (c) MontaVista Software, Inc. 2008. * Anton Vorontsov * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include "fhci.h" void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) { int i; if (usb_er == -1) { fhci->usb_irq_stat[12]++; return; } for (i = 0; i < 12; ++i) { if (usb_er & (1 << i)) fhci->usb_irq_stat[i]++; } } static int fhci_dfs_regs_show(struct seq_file *s, void *v) { struct fhci_hcd *fhci = s->private; struct qe_usb_ctlr __iomem *regs = fhci->regs; seq_printf(s, "mode: 0x%x\n" "addr: 0x%x\n" "command: 0x%x\n" "ep0: 0x%x\n" "event: 0x%x\n" "mask: 0x%x\n" "status: 0x%x\n" "SOF timer: %d\n" "frame number: %d\n" "lines status: 0x%x\n", in_8(®s->usb_usmod), in_8(®s->usb_usadr), in_8(®s->usb_uscom), in_be16(®s->usb_usep[0]), in_be16(®s->usb_usber), in_be16(®s->usb_usbmr), in_8(®s->usb_usbs), in_be16(®s->usb_ussft), in_be16(®s->usb_usfrn), fhci_ioports_check_bus_state(fhci)); return 0; } static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v) { struct fhci_hcd *fhci = s->private; int *usb_irq_stat = fhci->usb_irq_stat; seq_printf(s, "RXB: %d\n" "TXB: %d\n" "BSY: %d\n" "SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n" "TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n" "RESET: %d\n" "SFT: %d\n" "MSF: %d\n" "IDLE_ONLY: %d\n", usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2], usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5], usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8], usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11], usb_irq_stat[12]); return 0; } static int fhci_dfs_regs_open(struct inode *inode, struct file *file) { return single_open(file, fhci_dfs_regs_show, inode->i_private); } static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file) { return single_open(file, fhci_dfs_irq_stat_show, inode->i_private); } static const struct file_operations fhci_dfs_regs_fops = { .open = fhci_dfs_regs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations fhci_dfs_irq_stat_fops = { .open = fhci_dfs_irq_stat_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; void fhci_dfs_create(struct fhci_hcd *fhci) { struct device *dev = fhci_to_hcd(fhci)->self.controller; fhci->dfs_root = debugfs_create_dir(dev_name(dev), usb_debug_root); if (!fhci->dfs_root) { WARN_ON(1); return; } fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO, fhci->dfs_root, fhci, &fhci_dfs_regs_fops); fhci->dfs_irq_stat = debugfs_create_file("irq_stat", S_IFREG | S_IRUGO, fhci->dfs_root, fhci, &fhci_dfs_irq_stat_fops); WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat); } void fhci_dfs_destroy(struct fhci_hcd *fhci) { if (!fhci->dfs_root) return; debugfs_remove(fhci->dfs_irq_stat); debugfs_remove(fhci->dfs_regs); debugfs_remove(fhci->dfs_root); } t'>Commit message (Expand)AuthorFilesLines :space:mode:
authorDouglas Miller <dougmill@linux.vnet.ibm.com>2017-01-28 06:42:20 -0600
committerTejun Heo <tj@kernel.org>2017-01-28 07:49:42 -0500
commit966d2b04e070bc040319aaebfec09e0144dc3341 (patch)
tree4b96156e3d1dd4dfd6039b7c219c9dc4616da52d /drivers/usb/host/uhci-pci.c
parent1b1bc42c1692e9b62756323c675a44cb1a1f9dbd (diff)
percpu-refcount: fix reference leak during percpu-atomic transition
percpu_ref_tryget() and percpu_ref_tryget_live() should return "true" IFF they acquire a reference. But the return value from atomic_long_inc_not_zero() is a long and may have high bits set, e.g. PERCPU_COUNT_BIAS, and the return value of the tryget routines is bool so the reference may actually be acquired but the routines return "false" which results in a reference leak since the caller assumes it does not need to do a corresponding percpu_ref_put(). This was seen when performing CPU hotplug during I/O, as hangs in blk_mq_freeze_queue_wait where percpu_ref_kill (blk_mq_freeze_queue_start) raced with percpu_ref_tryget (blk_mq_timeout_work). Sample stack trace: __switch_to+0x2c0/0x450 __schedule+0x2f8/0x970 schedule+0x48/0xc0 blk_mq_freeze_queue_wait+0x94/0x120 blk_mq_queue_reinit_work+0xb8/0x180 blk_mq_queue_reinit_prepare+0x84/0xa0 cpuhp_invoke_callback+0x17c/0x600 cpuhp_up_callbacks+0x58/0x150 _cpu_up+0xf0/0x1c0 do_cpu_up+0x120/0x150 cpu_subsys_online+0x64/0xe0 device_online+0xb4/0x120 online_store+0xb4/0xc0 dev_attr_store+0x68/0xa0 sysfs_kf_write+0x80/0xb0 kernfs_fop_write+0x17c/0x250 __vfs_write+0x6c/0x1e0 vfs_write+0xd0/0x270 SyS_write+0x6c/0x110 system_call+0x38/0xe0 Examination of the queue showed a single reference (no PERCPU_COUNT_BIAS, and __PERCPU_REF_DEAD, __PERCPU_REF_ATOMIC set) and no requests. However, conditions at the time of the race are count of PERCPU_COUNT_BIAS + 0 and __PERCPU_REF_DEAD and __PERCPU_REF_ATOMIC set. The fix is to make the tryget routines use an actual boolean internally instead of the atomic long result truncated to a int. Fixes: e625305b3907 percpu-refcount: make percpu_ref based on longs instead of ints Link: https://bugzilla.kernel.org/show_bug.cgi?id=190751 Signed-off-by: Douglas Miller <dougmill@linux.vnet.ibm.com> Reviewed-by: Jens Axboe <axboe@fb.com> Signed-off-by: Tejun Heo <tj@kernel.org> Fixes: e625305b3907 ("percpu-refcount: make percpu_ref based on longs instead of ints") Cc: stable@vger.kernel.org # v3.18+
Diffstat (limited to 'drivers/usb/host/uhci-pci.c')