diff options
| -rw-r--r-- | drivers/video/fbmem.c | 106 | ||||
| -rw-r--r-- | include/linux/fb.h | 1 |
2 files changed, 81 insertions, 26 deletions
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index e0c2284924b6..ea16e654a9b6 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
| @@ -42,9 +42,34 @@ | |||
| 42 | 42 | ||
| 43 | #define FBPIXMAPSIZE (1024 * 8) | 43 | #define FBPIXMAPSIZE (1024 * 8) |
| 44 | 44 | ||
| 45 | static DEFINE_MUTEX(registration_lock); | ||
| 45 | struct fb_info *registered_fb[FB_MAX] __read_mostly; | 46 | struct fb_info *registered_fb[FB_MAX] __read_mostly; |
| 46 | int num_registered_fb __read_mostly; | 47 | int num_registered_fb __read_mostly; |
| 47 | 48 | ||
| 49 | static struct fb_info *get_fb_info(unsigned int idx) | ||
| 50 | { | ||
| 51 | struct fb_info *fb_info; | ||
| 52 | |||
| 53 | if (idx >= FB_MAX) | ||
| 54 | return ERR_PTR(-ENODEV); | ||
| 55 | |||
| 56 | mutex_lock(®istration_lock); | ||
| 57 | fb_info = registered_fb[idx]; | ||
| 58 | if (fb_info) | ||
| 59 | atomic_inc(&fb_info->count); | ||
| 60 | mutex_unlock(®istration_lock); | ||
| 61 | |||
| 62 | return fb_info; | ||
| 63 | } | ||
| 64 | |||
| 65 | static void put_fb_info(struct fb_info *fb_info) | ||
| 66 | { | ||
| 67 | if (!atomic_dec_and_test(&fb_info->count)) | ||
| 68 | return; | ||
| 69 | if (fb_info->fbops->fb_destroy) | ||
| 70 | fb_info->fbops->fb_destroy(fb_info); | ||
| 71 | } | ||
| 72 | |||
| 48 | int lock_fb_info(struct fb_info *info) | 73 | int lock_fb_info(struct fb_info *info) |
| 49 | { | 74 | { |
| 50 | mutex_lock(&info->lock); | 75 | mutex_lock(&info->lock); |
| @@ -647,6 +672,7 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; } | |||
| 647 | 672 | ||
| 648 | static void *fb_seq_start(struct seq_file *m, loff_t *pos) | 673 | static void *fb_seq_start(struct seq_file *m, loff_t *pos) |
| 649 | { | 674 | { |
| 675 | mutex_lock(®istration_lock); | ||
| 650 | return (*pos < FB_MAX) ? pos : NULL; | 676 | return (*pos < FB_MAX) ? pos : NULL; |
| 651 | } | 677 | } |
| 652 | 678 | ||
| @@ -658,6 +684,7 @@ static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 658 | 684 | ||
| 659 | static void fb_seq_stop(struct seq_file *m, void *v) | 685 | static void fb_seq_stop(struct seq_file *m, void *v) |
| 660 | { | 686 | { |
| 687 | mutex_unlock(®istration_lock); | ||
| 661 | } | 688 | } |
| 662 | 689 | ||
| 663 | static int fb_seq_show(struct seq_file *m, void *v) | 690 | static int fb_seq_show(struct seq_file *m, void *v) |
| @@ -690,13 +717,30 @@ static const struct file_operations fb_proc_fops = { | |||
| 690 | .release = seq_release, | 717 | .release = seq_release, |
| 691 | }; | 718 | }; |
| 692 | 719 | ||
| 693 | static ssize_t | 720 | /* |
| 694 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 721 | * We hold a reference to the fb_info in file->private_data, |
| 722 | * but if the current registered fb has changed, we don't | ||
| 723 | * actually want to use it. | ||
| 724 | * | ||
| 725 | * So look up the fb_info using the inode minor number, | ||
| 726 | * and just verify it against the reference we have. | ||
| 727 | */ | ||
| 728 | static struct fb_info *file_fb_info(struct file *file) | ||
| 695 | { | 729 | { |
| 696 | unsigned long p = *ppos; | ||
| 697 | struct inode *inode = file->f_path.dentry->d_inode; | 730 | struct inode *inode = file->f_path.dentry->d_inode; |
| 698 | int fbidx = iminor(inode); | 731 | int fbidx = iminor(inode); |
| 699 | struct fb_info *info = registered_fb[fbidx]; | 732 | struct fb_info *info = registered_fb[fbidx]; |
| 733 | |||
| 734 | if (info != file->private_data) | ||
| 735 | info = NULL; | ||
| 736 | return info; | ||
| 737 | } | ||
| 738 | |||
| 739 | static ssize_t | ||
| 740 | fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | ||
| 741 | { | ||
| 742 | unsigned long p = *ppos; | ||
| 743 | struct fb_info *info = file_fb_info(file); | ||
| 700 | u8 *buffer, *dst; | 744 | u8 *buffer, *dst; |
| 701 | u8 __iomem *src; | 745 | u8 __iomem *src; |
| 702 | int c, cnt = 0, err = 0; | 746 | int c, cnt = 0, err = 0; |
| @@ -761,9 +805,7 @@ static ssize_t | |||
| 761 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | 805 | fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) |
| 762 | { | 806 | { |
| 763 | unsigned long p = *ppos; | 807 | unsigned long p = *ppos; |
| 764 | struct inode *inode = file->f_path.dentry->d_inode; | 808 | struct fb_info *info = file_fb_info(file); |
| 765 | int fbidx = iminor(inode); | ||
| 766 | struct fb_info *info = registered_fb[fbidx]; | ||
| 767 | u8 *buffer, *src; | 809 | u8 *buffer, *src; |
| 768 | u8 __iomem *dst; | 810 | u8 __iomem *dst; |
| 769 | int c, cnt = 0, err = 0; | 811 | int c, cnt = 0, err = 0; |
| @@ -1141,10 +1183,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
| 1141 | 1183 | ||
| 1142 | static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 1184 | static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| 1143 | { | 1185 | { |
| 1144 | struct inode *inode = file->f_path.dentry->d_inode; | 1186 | struct fb_info *info = file_fb_info(file); |
| 1145 | int fbidx = iminor(inode); | ||
| 1146 | struct fb_info *info = registered_fb[fbidx]; | ||
| 1147 | 1187 | ||
| 1188 | if (!info) | ||
| 1189 | return -ENODEV; | ||
| 1148 | return do_fb_ioctl(info, cmd, arg); | 1190 | return do_fb_ioctl(info, cmd, arg); |
| 1149 | } | 1191 | } |
| 1150 | 1192 | ||
| @@ -1265,12 +1307,13 @@ static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, | |||
| 1265 | static long fb_compat_ioctl(struct file *file, unsigned int cmd, | 1307 | static long fb_compat_ioctl(struct file *file, unsigned int cmd, |
| 1266 | unsigned long arg) | 1308 | unsigned long arg) |
| 1267 | { | 1309 | { |
| 1268 | struct inode *inode = file->f_path.dentry->d_inode; | 1310 | struct fb_info *info = file_fb_info(file); |
| 1269 | int fbidx = iminor(inode); | 1311 | struct fb_ops *fb; |
| 1270 | struct fb_info *info = registered_fb[fbidx]; | ||
| 1271 | struct fb_ops *fb = info->fbops; | ||
| 1272 | long ret = -ENOIOCTLCMD; | 1312 | long ret = -ENOIOCTLCMD; |
| 1273 | 1313 | ||
| 1314 | if (!info) | ||
| 1315 | return -ENODEV; | ||
| 1316 | fb = info->fbops; | ||
| 1274 | switch(cmd) { | 1317 | switch(cmd) { |
| 1275 | case FBIOGET_VSCREENINFO: | 1318 | case FBIOGET_VSCREENINFO: |
| 1276 | case FBIOPUT_VSCREENINFO: | 1319 | case FBIOPUT_VSCREENINFO: |
| @@ -1303,16 +1346,18 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, | |||
| 1303 | static int | 1346 | static int |
| 1304 | fb_mmap(struct file *file, struct vm_area_struct * vma) | 1347 | fb_mmap(struct file *file, struct vm_area_struct * vma) |
| 1305 | { | 1348 | { |
| 1306 | int fbidx = iminor(file->f_path.dentry->d_inode); | 1349 | struct fb_info *info = file_fb_info(file); |
| 1307 | struct fb_info *info = registered_fb[fbidx]; | 1350 | struct fb_ops *fb; |
| 1308 | struct fb_ops *fb = info->fbops; | ||
| 1309 | unsigned long off; | 1351 | unsigned long off; |
| 1310 | unsigned long start; | 1352 | unsigned long start; |
| 1311 | u32 len; | 1353 | u32 len; |
| 1312 | 1354 | ||
| 1355 | if (!info) | ||
| 1356 | return -ENODEV; | ||
| 1313 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | 1357 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) |
| 1314 | return -EINVAL; | 1358 | return -EINVAL; |
| 1315 | off = vma->vm_pgoff << PAGE_SHIFT; | 1359 | off = vma->vm_pgoff << PAGE_SHIFT; |
| 1360 | fb = info->fbops; | ||
| 1316 | if (!fb) | 1361 | if (!fb) |
| 1317 | return -ENODEV; | 1362 | return -ENODEV; |
| 1318 | mutex_lock(&info->mm_lock); | 1363 | mutex_lock(&info->mm_lock); |
| @@ -1361,14 +1406,16 @@ __releases(&info->lock) | |||
| 1361 | struct fb_info *info; | 1406 | struct fb_info *info; |
| 1362 | int res = 0; | 1407 | int res = 0; |
| 1363 | 1408 | ||
| 1364 | if (fbidx >= FB_MAX) | 1409 | info = get_fb_info(fbidx); |
| 1365 | return -ENODEV; | 1410 | if (!info) { |
| 1366 | info = registered_fb[fbidx]; | ||
| 1367 | if (!info) | ||
| 1368 | request_module("fb%d", fbidx); | 1411 | request_module("fb%d", fbidx); |
| 1369 | info = registered_fb[fbidx]; | 1412 | info = get_fb_info(fbidx); |
| 1370 | if (!info) | 1413 | if (!info) |
| 1371 | return -ENODEV; | 1414 | return -ENODEV; |
| 1415 | } | ||
| 1416 | if (IS_ERR(info)) | ||
| 1417 | return PTR_ERR(info); | ||
| 1418 | |||
| 1372 | mutex_lock(&info->lock); | 1419 | mutex_lock(&info->lock); |
| 1373 | if (!try_module_get(info->fbops->owner)) { | 1420 | if (!try_module_get(info->fbops->owner)) { |
| 1374 | res = -ENODEV; | 1421 | res = -ENODEV; |
| @@ -1386,6 +1433,8 @@ __releases(&info->lock) | |||
| 1386 | #endif | 1433 | #endif |
| 1387 | out: | 1434 | out: |
| 1388 | mutex_unlock(&info->lock); | 1435 | mutex_unlock(&info->lock); |
| 1436 | if (res) | ||
| 1437 | put_fb_info(info); | ||
| 1389 | return res; | 1438 | return res; |
| 1390 | } | 1439 | } |
| 1391 | 1440 | ||
| @@ -1401,6 +1450,7 @@ __releases(&info->lock) | |||
| 1401 | info->fbops->fb_release(info,1); | 1450 | info->fbops->fb_release(info,1); |
| 1402 | module_put(info->fbops->owner); | 1451 | module_put(info->fbops->owner); |
| 1403 | mutex_unlock(&info->lock); | 1452 | mutex_unlock(&info->lock); |
| 1453 | put_fb_info(info); | ||
| 1404 | return 0; | 1454 | return 0; |
| 1405 | } | 1455 | } |
| 1406 | 1456 | ||
| @@ -1542,11 +1592,13 @@ register_framebuffer(struct fb_info *fb_info) | |||
| 1542 | remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, | 1592 | remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, |
| 1543 | fb_is_primary_device(fb_info)); | 1593 | fb_is_primary_device(fb_info)); |
| 1544 | 1594 | ||
| 1595 | mutex_lock(®istration_lock); | ||
| 1545 | num_registered_fb++; | 1596 | num_registered_fb++; |
| 1546 | for (i = 0 ; i < FB_MAX; i++) | 1597 | for (i = 0 ; i < FB_MAX; i++) |
| 1547 | if (!registered_fb[i]) | 1598 | if (!registered_fb[i]) |
| 1548 | break; | 1599 | break; |
| 1549 | fb_info->node = i; | 1600 | fb_info->node = i; |
| 1601 | atomic_set(&fb_info->count, 1); | ||
| 1550 | mutex_init(&fb_info->lock); | 1602 | mutex_init(&fb_info->lock); |
| 1551 | mutex_init(&fb_info->mm_lock); | 1603 | mutex_init(&fb_info->mm_lock); |
| 1552 | 1604 | ||
| @@ -1583,6 +1635,7 @@ register_framebuffer(struct fb_info *fb_info) | |||
| 1583 | fb_var_to_videomode(&mode, &fb_info->var); | 1635 | fb_var_to_videomode(&mode, &fb_info->var); |
| 1584 | fb_add_videomode(&mode, &fb_info->modelist); | 1636 | fb_add_videomode(&mode, &fb_info->modelist); |
| 1585 | registered_fb[i] = fb_info; | 1637 | registered_fb[i] = fb_info; |
| 1638 | mutex_unlock(®istration_lock); | ||
| 1586 | 1639 | ||
| 1587 | event.info = fb_info; | 1640 | event.info = fb_info; |
| 1588 | if (!lock_fb_info(fb_info)) | 1641 | if (!lock_fb_info(fb_info)) |
| @@ -1616,6 +1669,7 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
| 1616 | struct fb_event event; | 1669 | struct fb_event event; |
| 1617 | int i, ret = 0; | 1670 | int i, ret = 0; |
| 1618 | 1671 | ||
| 1672 | mutex_lock(®istration_lock); | ||
| 1619 | i = fb_info->node; | 1673 | i = fb_info->node; |
| 1620 | if (!registered_fb[i]) { | 1674 | if (!registered_fb[i]) { |
| 1621 | ret = -EINVAL; | 1675 | ret = -EINVAL; |
| @@ -1638,7 +1692,7 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
| 1638 | (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) | 1692 | (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) |
| 1639 | kfree(fb_info->pixmap.addr); | 1693 | kfree(fb_info->pixmap.addr); |
| 1640 | fb_destroy_modelist(&fb_info->modelist); | 1694 | fb_destroy_modelist(&fb_info->modelist); |
| 1641 | registered_fb[i]=NULL; | 1695 | registered_fb[i] = NULL; |
| 1642 | num_registered_fb--; | 1696 | num_registered_fb--; |
| 1643 | fb_cleanup_device(fb_info); | 1697 | fb_cleanup_device(fb_info); |
| 1644 | device_destroy(fb_class, MKDEV(FB_MAJOR, i)); | 1698 | device_destroy(fb_class, MKDEV(FB_MAJOR, i)); |
| @@ -1646,9 +1700,9 @@ unregister_framebuffer(struct fb_info *fb_info) | |||
| 1646 | fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); | 1700 | fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); |
| 1647 | 1701 | ||
| 1648 | /* this may free fb info */ | 1702 | /* this may free fb info */ |
| 1649 | if (fb_info->fbops->fb_destroy) | 1703 | put_fb_info(fb_info); |
| 1650 | fb_info->fbops->fb_destroy(fb_info); | ||
| 1651 | done: | 1704 | done: |
| 1705 | mutex_unlock(®istration_lock); | ||
| 1652 | return ret; | 1706 | return ret; |
| 1653 | } | 1707 | } |
| 1654 | 1708 | ||
diff --git a/include/linux/fb.h b/include/linux/fb.h index df728c1c29ed..6a8274877171 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h | |||
| @@ -832,6 +832,7 @@ struct fb_tile_ops { | |||
| 832 | #define FBINFO_CAN_FORCE_OUTPUT 0x200000 | 832 | #define FBINFO_CAN_FORCE_OUTPUT 0x200000 |
| 833 | 833 | ||
| 834 | struct fb_info { | 834 | struct fb_info { |
| 835 | atomic_t count; | ||
| 835 | int node; | 836 | int node; |
| 836 | int flags; | 837 | int flags; |
| 837 | struct mutex lock; /* Lock for open/release/ioctl funcs */ | 838 | struct mutex lock; /* Lock for open/release/ioctl funcs */ |
