aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2018-10-08 06:57:34 -0400
committerBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>2018-10-08 06:57:34 -0400
commit68a958a915ca912b8ce71b9eea7445996f6e681e (patch)
tree23476581d14a94613b449d7536f71a9511d6638f
parentad4366ad4831f9a42d86f19c43a482c7cb04cecb (diff)
udlfb: handle unplug properly
The udlfb driver maintained an open count and cleaned up itself when the count reached zero. But the console is also counted in the reference count - so, if the user unplugged the device, the open count would not drop to zero and the driver stayed loaded with console attached. If the user re-plugged the adapter, it would create a device /dev/fb1, show green screen and the access to the console would be lost. The framebuffer subsystem has reference counting on its own - in order to fix the unplug bug, we rely the framebuffer reference counting. When the user unplugs the adapter, we call unregister_framebuffer unconditionally. unregister_framebuffer will unbind the console, wait until all users stop using the framebuffer and then call the fb_destroy method. The fb_destroy cleans up the USB driver. This patch makes the following changes: * Drop dlfb->kref and rely on implicit framebuffer reference counting instead. * dlfb_usb_disconnect calls unregister_framebuffer, the rest of driver cleanup is done in the function dlfb_ops_destroy. dlfb_ops_destroy will be called by the framebuffer subsystem when no processes have the framebuffer open or mapped. * We don't use workqueue during initialization, but initialize directly from dlfb_usb_probe. The workqueue could race with dlfb_usb_disconnect and this racing would produce various kinds of memory corruption. * We use usb_get_dev and usb_put_dev to make sure that the USB subsystem doesn't free the device under us. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> cc: Dave Airlie <airlied@redhat.com> Cc: Bernie Thompson <bernie@plugable.com>, Cc: Ladislav Michl <ladis@linux-mips.org> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
-rw-r--r--drivers/video/fbdev/udlfb.c141
-rw-r--r--include/video/udlfb.h3
2 files changed, 37 insertions, 107 deletions
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index afbd6101c78e..070026a7e55a 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -916,8 +916,6 @@ static int dlfb_ops_open(struct fb_info *info, int user)
916 916
917 dlfb->fb_count++; 917 dlfb->fb_count++;
918 918
919 kref_get(&dlfb->kref);
920
921 if (fb_defio && (info->fbdefio == NULL)) { 919 if (fb_defio && (info->fbdefio == NULL)) {
922 /* enable defio at last moment if not disabled by client */ 920 /* enable defio at last moment if not disabled by client */
923 921
@@ -940,14 +938,17 @@ static int dlfb_ops_open(struct fb_info *info, int user)
940 return 0; 938 return 0;
941} 939}
942 940
943/* 941static void dlfb_ops_destroy(struct fb_info *info)
944 * Called when all client interfaces to start transactions have been disabled,
945 * and all references to our device instance (dlfb_data) are released.
946 * Every transaction must have a reference, so we know are fully spun down
947 */
948static void dlfb_free(struct kref *kref)
949{ 942{
950 struct dlfb_data *dlfb = container_of(kref, struct dlfb_data, kref); 943 struct dlfb_data *dlfb = info->par;
944
945 if (info->cmap.len != 0)
946 fb_dealloc_cmap(&info->cmap);
947 if (info->monspecs.modedb)
948 fb_destroy_modedb(info->monspecs.modedb);
949 vfree(info->screen_base);
950
951 fb_destroy_modelist(&info->modelist);
951 952
952 while (!list_empty(&dlfb->deferred_free)) { 953 while (!list_empty(&dlfb->deferred_free)) {
953 struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list); 954 struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list);
@@ -957,40 +958,13 @@ static void dlfb_free(struct kref *kref)
957 } 958 }
958 vfree(dlfb->backing_buffer); 959 vfree(dlfb->backing_buffer);
959 kfree(dlfb->edid); 960 kfree(dlfb->edid);
961 usb_put_dev(dlfb->udev);
960 kfree(dlfb); 962 kfree(dlfb);
961}
962
963static void dlfb_free_framebuffer(struct dlfb_data *dlfb)
964{
965 struct fb_info *info = dlfb->info;
966
967 if (info) {
968 unregister_framebuffer(info);
969
970 if (info->cmap.len != 0)
971 fb_dealloc_cmap(&info->cmap);
972 if (info->monspecs.modedb)
973 fb_destroy_modedb(info->monspecs.modedb);
974 vfree(info->screen_base);
975
976 fb_destroy_modelist(&info->modelist);
977
978 dlfb->info = NULL;
979
980 /* Assume info structure is freed after this point */
981 framebuffer_release(info);
982 }
983 963
984 /* ref taken in probe() as part of registering framebfufer */ 964 /* Assume info structure is freed after this point */
985 kref_put(&dlfb->kref, dlfb_free); 965 framebuffer_release(info);
986} 966}
987 967
988static void dlfb_free_framebuffer_work(struct work_struct *work)
989{
990 struct dlfb_data *dlfb = container_of(work, struct dlfb_data,
991 free_framebuffer_work.work);
992 dlfb_free_framebuffer(dlfb);
993}
994/* 968/*
995 * Assumes caller is holding info->lock mutex (for open and release at least) 969 * Assumes caller is holding info->lock mutex (for open and release at least)
996 */ 970 */
@@ -1000,10 +974,6 @@ static int dlfb_ops_release(struct fb_info *info, int user)
1000 974
1001 dlfb->fb_count--; 975 dlfb->fb_count--;
1002 976
1003 /* We can't free fb_info here - fbmem will touch it when we return */
1004 if (dlfb->virtualized && (dlfb->fb_count == 0))
1005 schedule_delayed_work(&dlfb->free_framebuffer_work, HZ);
1006
1007 if ((dlfb->fb_count == 0) && (info->fbdefio)) { 977 if ((dlfb->fb_count == 0) && (info->fbdefio)) {
1008 fb_deferred_io_cleanup(info); 978 fb_deferred_io_cleanup(info);
1009 kfree(info->fbdefio); 979 kfree(info->fbdefio);
@@ -1013,8 +983,6 @@ static int dlfb_ops_release(struct fb_info *info, int user)
1013 983
1014 dev_dbg(info->dev, "release, user=%d count=%d\n", user, dlfb->fb_count); 984 dev_dbg(info->dev, "release, user=%d count=%d\n", user, dlfb->fb_count);
1015 985
1016 kref_put(&dlfb->kref, dlfb_free);
1017
1018 return 0; 986 return 0;
1019} 987}
1020 988
@@ -1172,6 +1140,7 @@ static struct fb_ops dlfb_ops = {
1172 .fb_blank = dlfb_ops_blank, 1140 .fb_blank = dlfb_ops_blank,
1173 .fb_check_var = dlfb_ops_check_var, 1141 .fb_check_var = dlfb_ops_check_var,
1174 .fb_set_par = dlfb_ops_set_par, 1142 .fb_set_par = dlfb_ops_set_par,
1143 .fb_destroy = dlfb_ops_destroy,
1175}; 1144};
1176 1145
1177 1146
@@ -1615,12 +1584,13 @@ success:
1615 return true; 1584 return true;
1616} 1585}
1617 1586
1618static void dlfb_init_framebuffer_work(struct work_struct *work);
1619
1620static int dlfb_usb_probe(struct usb_interface *intf, 1587static int dlfb_usb_probe(struct usb_interface *intf,
1621 const struct usb_device_id *id) 1588 const struct usb_device_id *id)
1622{ 1589{
1590 int i;
1591 const struct device_attribute *attr;
1623 struct dlfb_data *dlfb; 1592 struct dlfb_data *dlfb;
1593 struct fb_info *info;
1624 int retval = -ENOMEM; 1594 int retval = -ENOMEM;
1625 struct usb_device *usbdev = interface_to_usbdev(intf); 1595 struct usb_device *usbdev = interface_to_usbdev(intf);
1626 1596
@@ -1631,10 +1601,9 @@ static int dlfb_usb_probe(struct usb_interface *intf,
1631 goto error; 1601 goto error;
1632 } 1602 }
1633 1603
1634 kref_init(&dlfb->kref); /* matching kref_put in usb .disconnect fn */
1635 INIT_LIST_HEAD(&dlfb->deferred_free); 1604 INIT_LIST_HEAD(&dlfb->deferred_free);
1636 1605
1637 dlfb->udev = usbdev; 1606 dlfb->udev = usb_get_dev(usbdev);
1638 usb_set_intfdata(intf, dlfb); 1607 usb_set_intfdata(intf, dlfb);
1639 1608
1640 dev_dbg(&intf->dev, "console enable=%d\n", console); 1609 dev_dbg(&intf->dev, "console enable=%d\n", console);
@@ -1657,42 +1626,6 @@ static int dlfb_usb_probe(struct usb_interface *intf,
1657 } 1626 }
1658 1627
1659 1628
1660 if (!dlfb_alloc_urb_list(dlfb, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
1661 retval = -ENOMEM;
1662 dev_err(&intf->dev, "unable to allocate urb list\n");
1663 goto error;
1664 }
1665
1666 kref_get(&dlfb->kref); /* matching kref_put in free_framebuffer_work */
1667
1668 /* We don't register a new USB class. Our client interface is dlfbev */
1669
1670 /* Workitem keep things fast & simple during USB enumeration */
1671 INIT_DELAYED_WORK(&dlfb->init_framebuffer_work,
1672 dlfb_init_framebuffer_work);
1673 schedule_delayed_work(&dlfb->init_framebuffer_work, 0);
1674
1675 return 0;
1676
1677error:
1678 if (dlfb) {
1679
1680 kref_put(&dlfb->kref, dlfb_free); /* last ref from kref_init */
1681
1682 /* dev has been deallocated. Do not dereference */
1683 }
1684
1685 return retval;
1686}
1687
1688static void dlfb_init_framebuffer_work(struct work_struct *work)
1689{
1690 int i, retval;
1691 struct fb_info *info;
1692 const struct device_attribute *attr;
1693 struct dlfb_data *dlfb = container_of(work, struct dlfb_data,
1694 init_framebuffer_work.work);
1695
1696 /* allocates framebuffer driver structure, not framebuffer memory */ 1629 /* allocates framebuffer driver structure, not framebuffer memory */
1697 info = framebuffer_alloc(0, &dlfb->udev->dev); 1630 info = framebuffer_alloc(0, &dlfb->udev->dev);
1698 if (!info) { 1631 if (!info) {
@@ -1706,17 +1639,22 @@ static void dlfb_init_framebuffer_work(struct work_struct *work)
1706 dlfb->ops = dlfb_ops; 1639 dlfb->ops = dlfb_ops;
1707 info->fbops = &dlfb->ops; 1640 info->fbops = &dlfb->ops;
1708 1641
1642 INIT_LIST_HEAD(&info->modelist);
1643
1644 if (!dlfb_alloc_urb_list(dlfb, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
1645 retval = -ENOMEM;
1646 dev_err(&intf->dev, "unable to allocate urb list\n");
1647 goto error;
1648 }
1649
1650 /* We don't register a new USB class. Our client interface is dlfbev */
1651
1709 retval = fb_alloc_cmap(&info->cmap, 256, 0); 1652 retval = fb_alloc_cmap(&info->cmap, 256, 0);
1710 if (retval < 0) { 1653 if (retval < 0) {
1711 dev_err(info->device, "cmap allocation failed: %d\n", retval); 1654 dev_err(info->device, "cmap allocation failed: %d\n", retval);
1712 goto error; 1655 goto error;
1713 } 1656 }
1714 1657
1715 INIT_DELAYED_WORK(&dlfb->free_framebuffer_work,
1716 dlfb_free_framebuffer_work);
1717
1718 INIT_LIST_HEAD(&info->modelist);
1719
1720 retval = dlfb_setup_modes(dlfb, info, NULL, 0); 1658 retval = dlfb_setup_modes(dlfb, info, NULL, 0);
1721 if (retval != 0) { 1659 if (retval != 0) {
1722 dev_err(info->device, 1660 dev_err(info->device,
@@ -1760,10 +1698,16 @@ static void dlfb_init_framebuffer_work(struct work_struct *work)
1760 dev_name(info->dev), info->var.xres, info->var.yres, 1698 dev_name(info->dev), info->var.xres, info->var.yres,
1761 ((dlfb->backing_buffer) ? 1699 ((dlfb->backing_buffer) ?
1762 info->fix.smem_len * 2 : info->fix.smem_len) >> 10); 1700 info->fix.smem_len * 2 : info->fix.smem_len) >> 10);
1763 return; 1701 return 0;
1764 1702
1765error: 1703error:
1766 dlfb_free_framebuffer(dlfb); 1704 if (dlfb->info) {
1705 dlfb_ops_destroy(dlfb->info);
1706 } else if (dlfb) {
1707 usb_put_dev(dlfb->udev);
1708 kfree(dlfb);
1709 }
1710 return retval;
1767} 1711}
1768 1712
1769static void dlfb_usb_disconnect(struct usb_interface *intf) 1713static void dlfb_usb_disconnect(struct usb_interface *intf)
@@ -1791,20 +1735,9 @@ static void dlfb_usb_disconnect(struct usb_interface *intf)
1791 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) 1735 for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
1792 device_remove_file(info->dev, &fb_device_attrs[i]); 1736 device_remove_file(info->dev, &fb_device_attrs[i]);
1793 device_remove_bin_file(info->dev, &edid_attr); 1737 device_remove_bin_file(info->dev, &edid_attr);
1794 unlink_framebuffer(info);
1795 } 1738 }
1796 1739
1797 usb_set_intfdata(intf, NULL); 1740 unregister_framebuffer(info);
1798 dlfb->udev = NULL;
1799
1800 /* if clients still have us open, will be freed on last close */
1801 if (dlfb->fb_count == 0)
1802 schedule_delayed_work(&dlfb->free_framebuffer_work, 0);
1803
1804 /* release reference taken by kref_init in probe() */
1805 kref_put(&dlfb->kref, dlfb_free);
1806
1807 /* consider dlfb_data freed */
1808} 1741}
1809 1742
1810static struct usb_driver dlfb_driver = { 1743static struct usb_driver dlfb_driver = {
diff --git a/include/video/udlfb.h b/include/video/udlfb.h
index 3abd327bada6..7d09e54ae54e 100644
--- a/include/video/udlfb.h
+++ b/include/video/udlfb.h
@@ -36,12 +36,9 @@ struct dlfb_data {
36 struct usb_device *udev; 36 struct usb_device *udev;
37 struct fb_info *info; 37 struct fb_info *info;
38 struct urb_list urbs; 38 struct urb_list urbs;
39 struct kref kref;
40 char *backing_buffer; 39 char *backing_buffer;
41 int fb_count; 40 int fb_count;
42 bool virtualized; /* true when physical usb device not present */ 41 bool virtualized; /* true when physical usb device not present */
43 struct delayed_work init_framebuffer_work;
44 struct delayed_work free_framebuffer_work;
45 atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ 42 atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */
46 atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ 43 atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
47 char *edid; /* null until we read edid from hw or get from sysfs */ 44 char *edid; /* null until we read edid from hw or get from sysfs */