aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbmem.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-05-13 19:16:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-14 13:23:44 -0400
commit712f3147aee0fbbbbed2da20b21b272c5505125e (patch)
tree02bf6ff00f1978b4165c2c2d4554606bae65107e /drivers/video/fbmem.c
parentc47747fde931c02455683bd00ea43eaa62f35b0e (diff)
fbmem: fix remove_conflicting_framebuffers races
When a register_framebuffer() call results in us removing old conflicting framebuffers, the new registration_lock doesn't protect that situation. And we can't just add the same locking to the function, because these functions call each other: register_framebuffer() calls remove_conflicting_framebuffers, which in turn calls unregister_framebuffer for any conflicting entry. In order to fix it, this just creates wrapper functions around all three functions and makes the versions that actually do the work be called "do_xxx()", leaving just the wrapper that gets the lock and calls the worker function. So the rule becomes simply that "do_xxxx()" has to be called with the lock held, and now do_register_framebuffer() can just call do_remove_conflicting_framebuffers(), and that in turn can call _do_unregister_framebuffer(), and there is no deadlock, and we can hold the registration lock over the whole sequence, fixing the races. It also makes error cases simpler, and fixes one situation where we would return from unregister_framebuffer() without releasing the lock, pointed out by Bruno Prémont. Tested-by: Bruno Prémont <bonbons@linux-vserver.org> Tested-by: Anca Emanuel <anca.emanuel@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/fbmem.c')
-rw-r--r--drivers/video/fbmem.c117
1 files changed, 68 insertions, 49 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index ea16e654a9b6..46ee5e5a08c6 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1537,8 +1537,10 @@ static bool fb_do_apertures_overlap(struct apertures_struct *gena,
1537 return false; 1537 return false;
1538} 1538}
1539 1539
1540static int do_unregister_framebuffer(struct fb_info *fb_info);
1541
1540#define VGA_FB_PHYS 0xA0000 1542#define VGA_FB_PHYS 0xA0000
1541void remove_conflicting_framebuffers(struct apertures_struct *a, 1543static void do_remove_conflicting_framebuffers(struct apertures_struct *a,
1542 const char *name, bool primary) 1544 const char *name, bool primary)
1543{ 1545{
1544 int i; 1546 int i;
@@ -1560,24 +1562,12 @@ void remove_conflicting_framebuffers(struct apertures_struct *a,
1560 printk(KERN_INFO "fb: conflicting fb hw usage " 1562 printk(KERN_INFO "fb: conflicting fb hw usage "
1561 "%s vs %s - removing generic driver\n", 1563 "%s vs %s - removing generic driver\n",
1562 name, registered_fb[i]->fix.id); 1564 name, registered_fb[i]->fix.id);
1563 unregister_framebuffer(registered_fb[i]); 1565 do_unregister_framebuffer(registered_fb[i]);
1564 } 1566 }
1565 } 1567 }
1566} 1568}
1567EXPORT_SYMBOL(remove_conflicting_framebuffers);
1568
1569/**
1570 * register_framebuffer - registers a frame buffer device
1571 * @fb_info: frame buffer info structure
1572 *
1573 * Registers a frame buffer device @fb_info.
1574 *
1575 * Returns negative errno on error, or zero for success.
1576 *
1577 */
1578 1569
1579int 1570static int do_register_framebuffer(struct fb_info *fb_info)
1580register_framebuffer(struct fb_info *fb_info)
1581{ 1571{
1582 int i; 1572 int i;
1583 struct fb_event event; 1573 struct fb_event event;
@@ -1589,10 +1579,9 @@ register_framebuffer(struct fb_info *fb_info)
1589 if (fb_check_foreignness(fb_info)) 1579 if (fb_check_foreignness(fb_info))
1590 return -ENOSYS; 1580 return -ENOSYS;
1591 1581
1592 remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, 1582 do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
1593 fb_is_primary_device(fb_info)); 1583 fb_is_primary_device(fb_info));
1594 1584
1595 mutex_lock(&registration_lock);
1596 num_registered_fb++; 1585 num_registered_fb++;
1597 for (i = 0 ; i < FB_MAX; i++) 1586 for (i = 0 ; i < FB_MAX; i++)
1598 if (!registered_fb[i]) 1587 if (!registered_fb[i])
@@ -1635,7 +1624,6 @@ register_framebuffer(struct fb_info *fb_info)
1635 fb_var_to_videomode(&mode, &fb_info->var); 1624 fb_var_to_videomode(&mode, &fb_info->var);
1636 fb_add_videomode(&mode, &fb_info->modelist); 1625 fb_add_videomode(&mode, &fb_info->modelist);
1637 registered_fb[i] = fb_info; 1626 registered_fb[i] = fb_info;
1638 mutex_unlock(&registration_lock);
1639 1627
1640 event.info = fb_info; 1628 event.info = fb_info;
1641 if (!lock_fb_info(fb_info)) 1629 if (!lock_fb_info(fb_info))
@@ -1645,37 +1633,14 @@ register_framebuffer(struct fb_info *fb_info)
1645 return 0; 1633 return 0;
1646} 1634}
1647 1635
1648 1636static int do_unregister_framebuffer(struct fb_info *fb_info)
1649/**
1650 * unregister_framebuffer - releases a frame buffer device
1651 * @fb_info: frame buffer info structure
1652 *
1653 * Unregisters a frame buffer device @fb_info.
1654 *
1655 * Returns negative errno on error, or zero for success.
1656 *
1657 * This function will also notify the framebuffer console
1658 * to release the driver.
1659 *
1660 * This is meant to be called within a driver's module_exit()
1661 * function. If this is called outside module_exit(), ensure
1662 * that the driver implements fb_open() and fb_release() to
1663 * check that no processes are using the device.
1664 */
1665
1666int
1667unregister_framebuffer(struct fb_info *fb_info)
1668{ 1637{
1669 struct fb_event event; 1638 struct fb_event event;
1670 int i, ret = 0; 1639 int i, ret = 0;
1671 1640
1672 mutex_lock(&registration_lock);
1673 i = fb_info->node; 1641 i = fb_info->node;
1674 if (!registered_fb[i]) { 1642 if (!registered_fb[i])
1675 ret = -EINVAL; 1643 return -EINVAL;
1676 goto done;
1677 }
1678
1679 1644
1680 if (!lock_fb_info(fb_info)) 1645 if (!lock_fb_info(fb_info))
1681 return -ENODEV; 1646 return -ENODEV;
@@ -1683,10 +1648,8 @@ unregister_framebuffer(struct fb_info *fb_info)
1683 ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); 1648 ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);
1684 unlock_fb_info(fb_info); 1649 unlock_fb_info(fb_info);
1685 1650
1686 if (ret) { 1651 if (ret)
1687 ret = -EINVAL; 1652 return -EINVAL;
1688 goto done;
1689 }
1690 1653
1691 if (fb_info->pixmap.addr && 1654 if (fb_info->pixmap.addr &&
1692 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) 1655 (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
@@ -1701,8 +1664,64 @@ unregister_framebuffer(struct fb_info *fb_info)
1701 1664
1702 /* this may free fb info */ 1665 /* this may free fb info */
1703 put_fb_info(fb_info); 1666 put_fb_info(fb_info);
1704done: 1667 return 0;
1668}
1669
1670void remove_conflicting_framebuffers(struct apertures_struct *a,
1671 const char *name, bool primary)
1672{
1673 mutex_lock(&registration_lock);
1674 do_remove_conflicting_framebuffers(a, name, primary);
1705 mutex_unlock(&registration_lock); 1675 mutex_unlock(&registration_lock);
1676}
1677EXPORT_SYMBOL(remove_conflicting_framebuffers);
1678
1679/**
1680 * register_framebuffer - registers a frame buffer device
1681 * @fb_info: frame buffer info structure
1682 *
1683 * Registers a frame buffer device @fb_info.
1684 *
1685 * Returns negative errno on error, or zero for success.
1686 *
1687 */
1688int
1689register_framebuffer(struct fb_info *fb_info)
1690{
1691 int ret;
1692
1693 mutex_lock(&registration_lock);
1694 ret = do_register_framebuffer(fb_info);
1695 mutex_unlock(&registration_lock);
1696
1697 return ret;
1698}
1699
1700/**
1701 * unregister_framebuffer - releases a frame buffer device
1702 * @fb_info: frame buffer info structure
1703 *
1704 * Unregisters a frame buffer device @fb_info.
1705 *
1706 * Returns negative errno on error, or zero for success.
1707 *
1708 * This function will also notify the framebuffer console
1709 * to release the driver.
1710 *
1711 * This is meant to be called within a driver's module_exit()
1712 * function. If this is called outside module_exit(), ensure
1713 * that the driver implements fb_open() and fb_release() to
1714 * check that no processes are using the device.
1715 */
1716int
1717unregister_framebuffer(struct fb_info *fb_info)
1718{
1719 int ret;
1720
1721 mutex_lock(&registration_lock);
1722 ret = do_unregister_framebuffer(fb_info);
1723 mutex_unlock(&registration_lock);
1724
1706 return ret; 1725 return ret;
1707} 1726}
1708 1727