Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udlfb: fix hcd_buffer_free panic on unplug/replug

Fix race conditions with unplug/replug behavior, in particular
take care not to hold up USB probe/disconnect for long-running
framebuffer operations and rely on usb to handle teardown.

Fix for kernel panic reported with new F17 multiseat support.

Reported-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Bernie Thompson <bernie@plugable.com>

+81 -66
+80 -66
drivers/video/udlfb.c
··· 918 918 { 919 919 struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); 920 920 921 - /* this function will wait for all in-flight urbs to complete */ 922 - if (dev->urbs.count > 0) 923 - dlfb_free_urb_list(dev); 924 - 925 921 if (dev->backing_buffer) 926 922 vfree(dev->backing_buffer); 927 923 ··· 936 940 up(&unode->dev->urbs.limit_sem); 937 941 } 938 942 939 - static void dlfb_free_framebuffer_work(struct work_struct *work) 943 + static void dlfb_free_framebuffer(struct dlfb_data *dev) 940 944 { 941 - struct dlfb_data *dev = container_of(work, struct dlfb_data, 942 - free_framebuffer_work.work); 943 945 struct fb_info *info = dev->info; 944 - int node = info->node; 945 946 946 - unregister_framebuffer(info); 947 + if (info) { 948 + int node = info->node; 947 949 948 - if (info->cmap.len != 0) 949 - fb_dealloc_cmap(&info->cmap); 950 - if (info->monspecs.modedb) 951 - fb_destroy_modedb(info->monspecs.modedb); 952 - if (info->screen_base) 953 - vfree(info->screen_base); 950 + unregister_framebuffer(info); 954 951 955 - fb_destroy_modelist(&info->modelist); 952 + if (info->cmap.len != 0) 953 + fb_dealloc_cmap(&info->cmap); 954 + if (info->monspecs.modedb) 955 + fb_destroy_modedb(info->monspecs.modedb); 956 + if (info->screen_base) 957 + vfree(info->screen_base); 956 958 957 - dev->info = 0; 959 + fb_destroy_modelist(&info->modelist); 958 960 959 - /* Assume info structure is freed after this point */ 960 - framebuffer_release(info); 961 + dev->info = NULL; 961 962 962 - pr_warn("fb_info for /dev/fb%d has been freed\n", node); 963 + /* Assume info structure is freed after this point */ 964 + framebuffer_release(info); 965 + 966 + pr_warn("fb_info for /dev/fb%d has been freed\n", node); 967 + } 963 968 964 969 /* ref taken in probe() as part of registering framebfufer */ 965 970 kref_put(&dev->kref, dlfb_free); 966 971 } 967 972 973 + static void dlfb_free_framebuffer_work(struct work_struct *work) 974 + { 975 + struct dlfb_data *dev = container_of(work, struct dlfb_data, 976 + free_framebuffer_work.work); 977 + dlfb_free_framebuffer(dev); 978 + } 968 979 /* 969 980 * Assumes caller is holding info->lock mutex (for open and release at least) 970 981 */ ··· 1574 1571 kfree(buf); 1575 1572 return true; 1576 1573 } 1574 + 1575 + static void dlfb_init_framebuffer_work(struct work_struct *work); 1576 + 1577 1577 static int dlfb_usb_probe(struct usb_interface *interface, 1578 1578 const struct usb_device_id *id) 1579 1579 { 1580 1580 struct usb_device *usbdev; 1581 1581 struct dlfb_data *dev = 0; 1582 - struct fb_info *info = 0; 1583 1582 int retval = -ENOMEM; 1584 - int i; 1585 1583 1586 1584 /* usb initialization */ 1587 1585 ··· 1594 1590 goto error; 1595 1591 } 1596 1592 1597 - /* we need to wait for both usb and fbdev to spin down on disconnect */ 1598 1593 kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ 1599 - kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ 1600 1594 1601 1595 dev->udev = usbdev; 1602 1596 dev->gdev = &usbdev->dev; /* our generic struct device * */ ··· 1622 1620 goto error; 1623 1621 } 1624 1622 1623 + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ 1624 + 1625 1625 /* We don't register a new USB class. Our client interface is fbdev */ 1626 1626 1627 + /* Workitem keep things fast & simple during USB enumeration */ 1628 + INIT_DELAYED_WORK(&dev->init_framebuffer_work, 1629 + dlfb_init_framebuffer_work); 1630 + schedule_delayed_work(&dev->init_framebuffer_work, 0); 1631 + 1632 + return 0; 1633 + 1634 + error: 1635 + if (dev) { 1636 + 1637 + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ 1638 + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ 1639 + 1640 + /* dev has been deallocated. Do not dereference */ 1641 + } 1642 + 1643 + return retval; 1644 + } 1645 + 1646 + static void dlfb_init_framebuffer_work(struct work_struct *work) 1647 + { 1648 + struct dlfb_data *dev = container_of(work, struct dlfb_data, 1649 + init_framebuffer_work.work); 1650 + struct fb_info *info; 1651 + int retval; 1652 + int i; 1653 + 1627 1654 /* allocates framebuffer driver structure, not framebuffer memory */ 1628 - info = framebuffer_alloc(0, &interface->dev); 1655 + info = framebuffer_alloc(0, dev->gdev); 1629 1656 if (!info) { 1630 1657 retval = -ENOMEM; 1631 1658 pr_err("framebuffer_alloc failed\n"); ··· 1700 1669 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) { 1701 1670 retval = device_create_file(info->dev, &fb_device_attrs[i]); 1702 1671 if (retval) { 1703 - pr_err("device_create_file failed %d\n", retval); 1704 - goto err_del_attrs; 1672 + pr_warn("device_create_file failed %d\n", retval); 1705 1673 } 1706 1674 } 1707 1675 1708 1676 retval = device_create_bin_file(info->dev, &edid_attr); 1709 1677 if (retval) { 1710 - pr_err("device_create_bin_file failed %d\n", retval); 1711 - goto err_del_attrs; 1678 + pr_warn("device_create_bin_file failed %d\n", retval); 1712 1679 } 1713 1680 1714 1681 pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." ··· 1714 1685 info->var.xres, info->var.yres, 1715 1686 ((dev->backing_buffer) ? 1716 1687 info->fix.smem_len * 2 : info->fix.smem_len) >> 10); 1717 - return 0; 1718 - 1719 - err_del_attrs: 1720 - for (i -= 1; i >= 0; i--) 1721 - device_remove_file(info->dev, &fb_device_attrs[i]); 1688 + return; 1722 1689 1723 1690 error: 1724 - if (dev) { 1725 - 1726 - if (info) { 1727 - if (info->cmap.len != 0) 1728 - fb_dealloc_cmap(&info->cmap); 1729 - if (info->monspecs.modedb) 1730 - fb_destroy_modedb(info->monspecs.modedb); 1731 - if (info->screen_base) 1732 - vfree(info->screen_base); 1733 - 1734 - fb_destroy_modelist(&info->modelist); 1735 - 1736 - framebuffer_release(info); 1737 - } 1738 - 1739 - if (dev->backing_buffer) 1740 - vfree(dev->backing_buffer); 1741 - 1742 - kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ 1743 - kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ 1744 - 1745 - /* dev has been deallocated. Do not dereference */ 1746 - } 1747 - 1748 - return retval; 1691 + dlfb_free_framebuffer(dev); 1749 1692 } 1750 1693 1751 1694 static void dlfb_usb_disconnect(struct usb_interface *interface) ··· 1737 1736 /* When non-active we'll update virtual framebuffer, but no new urbs */ 1738 1737 atomic_set(&dev->usb_active, 0); 1739 1738 1740 - /* remove udlfb's sysfs interfaces */ 1741 - for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 1742 - device_remove_file(info->dev, &fb_device_attrs[i]); 1743 - device_remove_bin_file(info->dev, &edid_attr); 1744 - unlink_framebuffer(info); 1739 + /* this function will wait for all in-flight urbs to complete */ 1740 + dlfb_free_urb_list(dev); 1741 + 1742 + if (info) { 1743 + 1744 + /* remove udlfb's sysfs interfaces */ 1745 + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 1746 + device_remove_file(info->dev, &fb_device_attrs[i]); 1747 + device_remove_bin_file(info->dev, &edid_attr); 1748 + 1749 + /* it's safe to uncomment next line if your kernel 1750 + doesn't yet have this function exported */ 1751 + unlink_framebuffer(info); 1752 + } 1753 + 1745 1754 usb_set_intfdata(interface, NULL); 1755 + dev->udev = NULL; 1756 + dev->gdev = NULL; 1746 1757 1747 1758 /* if clients still have us open, will be freed on last close */ 1748 1759 if (dev->fb_count == 0) ··· 1820 1807 int ret; 1821 1808 unsigned long flags; 1822 1809 1823 - pr_notice("Waiting for completes and freeing all render urbs\n"); 1810 + pr_notice("Freeing all render urbs\n"); 1824 1811 1825 1812 /* keep waiting and freeing, until we've got 'em all */ 1826 1813 while (count--) { 1827 1814 1828 - /* Getting interrupted means a leak, but ok at shutdown*/ 1815 + /* Getting interrupted means a leak, but ok at disconnect */ 1829 1816 ret = down_interruptible(&dev->urbs.limit_sem); 1830 1817 if (ret) 1831 1818 break; ··· 1847 1834 kfree(node); 1848 1835 } 1849 1836 1837 + dev->urbs.count = 0; 1850 1838 } 1851 1839 1852 1840 static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size)
+1
include/video/udlfb.h
··· 41 41 char *backing_buffer; 42 42 int fb_count; 43 43 bool virtualized; /* true when physical usb device not present */ 44 + struct delayed_work init_framebuffer_work; 44 45 struct delayed_work free_framebuffer_work; 45 46 atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ 46 47 atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */