diff options
author | Andrea Righi <righi.andrea@gmail.com> | 2009-03-31 18:25:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-01 11:59:29 -0400 |
commit | 66c1ca019078220dc1bf968f2bb18421100ef147 (patch) | |
tree | b01d28d74bab9d9e3f0f24841a80a272d6f6520a /drivers | |
parent | 8636a9240cc93efa6b36f4cfe6253e0574f832c6 (diff) |
fbmem: fix fb_info->lock and mm->mmap_sem circular locking dependency
Fix a circular locking dependency in the frame buffer console driver
pushing down the mutex fb_info->lock.
Circular locking dependecies occur calling the blocking
fb_notifier_call_chain() with fb_info->lock held. Notifier callbacks can
try to acquire mm->mmap_sem, while fb_mmap() acquires the locks in the
reverse order mm->mmap_sem => fb_info->lock.
Tested-by: Andrey Borzenkov <arvidjaar@mail.ru>
Signed-off-by: Andrea Righi <righi.andrea@gmail.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Krzysztof Helt <krzysztof.h1@poczta.fm>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/backlight/backlight.c | 3 | ||||
-rw-r--r-- | drivers/video/backlight/lcd.c | 3 | ||||
-rw-r--r-- | drivers/video/console/fbcon.c | 73 | ||||
-rw-r--r-- | drivers/video/fbmem.c | 11 |
4 files changed, 70 insertions, 20 deletions
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 157057c79ca3..dd37cbcaf8ce 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c | |||
@@ -35,6 +35,8 @@ static int fb_notifier_callback(struct notifier_block *self, | |||
35 | return 0; | 35 | return 0; |
36 | 36 | ||
37 | bd = container_of(self, struct backlight_device, fb_notif); | 37 | bd = container_of(self, struct backlight_device, fb_notif); |
38 | if (!lock_fb_info(evdata->info)) | ||
39 | return -ENODEV; | ||
38 | mutex_lock(&bd->ops_lock); | 40 | mutex_lock(&bd->ops_lock); |
39 | if (bd->ops) | 41 | if (bd->ops) |
40 | if (!bd->ops->check_fb || | 42 | if (!bd->ops->check_fb || |
@@ -47,6 +49,7 @@ static int fb_notifier_callback(struct notifier_block *self, | |||
47 | backlight_update_status(bd); | 49 | backlight_update_status(bd); |
48 | } | 50 | } |
49 | mutex_unlock(&bd->ops_lock); | 51 | mutex_unlock(&bd->ops_lock); |
52 | unlock_fb_info(evdata->info); | ||
50 | return 0; | 53 | return 0; |
51 | } | 54 | } |
52 | 55 | ||
diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b6449470106c..0bb13df0fa89 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c | |||
@@ -40,6 +40,8 @@ static int fb_notifier_callback(struct notifier_block *self, | |||
40 | if (!ld->ops) | 40 | if (!ld->ops) |
41 | return 0; | 41 | return 0; |
42 | 42 | ||
43 | if (!lock_fb_info(evdata->info)) | ||
44 | return -ENODEV; | ||
43 | mutex_lock(&ld->ops_lock); | 45 | mutex_lock(&ld->ops_lock); |
44 | if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { | 46 | if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { |
45 | if (event == FB_EVENT_BLANK) { | 47 | if (event == FB_EVENT_BLANK) { |
@@ -51,6 +53,7 @@ static int fb_notifier_callback(struct notifier_block *self, | |||
51 | } | 53 | } |
52 | } | 54 | } |
53 | mutex_unlock(&ld->ops_lock); | 55 | mutex_unlock(&ld->ops_lock); |
56 | unlock_fb_info(evdata->info); | ||
54 | return 0; | 57 | return 0; |
55 | } | 58 | } |
56 | 59 | ||
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 1657b9608b04..2cd500a304f2 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
@@ -2954,8 +2954,11 @@ static int fbcon_fb_unbind(int idx) | |||
2954 | 2954 | ||
2955 | static int fbcon_fb_unregistered(struct fb_info *info) | 2955 | static int fbcon_fb_unregistered(struct fb_info *info) |
2956 | { | 2956 | { |
2957 | int i, idx = info->node; | 2957 | int i, idx; |
2958 | 2958 | ||
2959 | if (!lock_fb_info(info)) | ||
2960 | return -ENODEV; | ||
2961 | idx = info->node; | ||
2959 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 2962 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2960 | if (con2fb_map[i] == idx) | 2963 | if (con2fb_map[i] == idx) |
2961 | con2fb_map[i] = -1; | 2964 | con2fb_map[i] = -1; |
@@ -2979,13 +2982,14 @@ static int fbcon_fb_unregistered(struct fb_info *info) | |||
2979 | } | 2982 | } |
2980 | } | 2983 | } |
2981 | 2984 | ||
2982 | if (!num_registered_fb) | ||
2983 | unregister_con_driver(&fb_con); | ||
2984 | |||
2985 | |||
2986 | if (primary_device == idx) | 2985 | if (primary_device == idx) |
2987 | primary_device = -1; | 2986 | primary_device = -1; |
2988 | 2987 | ||
2988 | unlock_fb_info(info); | ||
2989 | |||
2990 | if (!num_registered_fb) | ||
2991 | unregister_con_driver(&fb_con); | ||
2992 | |||
2989 | return 0; | 2993 | return 0; |
2990 | } | 2994 | } |
2991 | 2995 | ||
@@ -3021,9 +3025,13 @@ static inline void fbcon_select_primary(struct fb_info *info) | |||
3021 | 3025 | ||
3022 | static int fbcon_fb_registered(struct fb_info *info) | 3026 | static int fbcon_fb_registered(struct fb_info *info) |
3023 | { | 3027 | { |
3024 | int ret = 0, i, idx = info->node; | 3028 | int ret = 0, i, idx; |
3025 | 3029 | ||
3030 | if (!lock_fb_info(info)) | ||
3031 | return -ENODEV; | ||
3032 | idx = info->node; | ||
3026 | fbcon_select_primary(info); | 3033 | fbcon_select_primary(info); |
3034 | unlock_fb_info(info); | ||
3027 | 3035 | ||
3028 | if (info_idx == -1) { | 3036 | if (info_idx == -1) { |
3029 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3037 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
@@ -3124,7 +3132,7 @@ static void fbcon_get_requirement(struct fb_info *info, | |||
3124 | } | 3132 | } |
3125 | } | 3133 | } |
3126 | 3134 | ||
3127 | static int fbcon_event_notify(struct notifier_block *self, | 3135 | static int fbcon_event_notify(struct notifier_block *self, |
3128 | unsigned long action, void *data) | 3136 | unsigned long action, void *data) |
3129 | { | 3137 | { |
3130 | struct fb_event *event = data; | 3138 | struct fb_event *event = data; |
@@ -3132,7 +3140,7 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
3132 | struct fb_videomode *mode; | 3140 | struct fb_videomode *mode; |
3133 | struct fb_con2fbmap *con2fb; | 3141 | struct fb_con2fbmap *con2fb; |
3134 | struct fb_blit_caps *caps; | 3142 | struct fb_blit_caps *caps; |
3135 | int ret = 0; | 3143 | int idx, ret = 0; |
3136 | 3144 | ||
3137 | /* | 3145 | /* |
3138 | * ignore all events except driver registration and deregistration | 3146 | * ignore all events except driver registration and deregistration |
@@ -3144,23 +3152,54 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
3144 | 3152 | ||
3145 | switch(action) { | 3153 | switch(action) { |
3146 | case FB_EVENT_SUSPEND: | 3154 | case FB_EVENT_SUSPEND: |
3155 | if (!lock_fb_info(info)) { | ||
3156 | ret = -ENODEV; | ||
3157 | goto done; | ||
3158 | } | ||
3147 | fbcon_suspended(info); | 3159 | fbcon_suspended(info); |
3160 | unlock_fb_info(info); | ||
3148 | break; | 3161 | break; |
3149 | case FB_EVENT_RESUME: | 3162 | case FB_EVENT_RESUME: |
3163 | if (!lock_fb_info(info)) { | ||
3164 | ret = -ENODEV; | ||
3165 | goto done; | ||
3166 | } | ||
3150 | fbcon_resumed(info); | 3167 | fbcon_resumed(info); |
3168 | unlock_fb_info(info); | ||
3151 | break; | 3169 | break; |
3152 | case FB_EVENT_MODE_CHANGE: | 3170 | case FB_EVENT_MODE_CHANGE: |
3171 | if (!lock_fb_info(info)) { | ||
3172 | ret = -ENODEV; | ||
3173 | goto done; | ||
3174 | } | ||
3153 | fbcon_modechanged(info); | 3175 | fbcon_modechanged(info); |
3176 | unlock_fb_info(info); | ||
3154 | break; | 3177 | break; |
3155 | case FB_EVENT_MODE_CHANGE_ALL: | 3178 | case FB_EVENT_MODE_CHANGE_ALL: |
3179 | if (!lock_fb_info(info)) { | ||
3180 | ret = -ENODEV; | ||
3181 | goto done; | ||
3182 | } | ||
3156 | fbcon_set_all_vcs(info); | 3183 | fbcon_set_all_vcs(info); |
3184 | unlock_fb_info(info); | ||
3157 | break; | 3185 | break; |
3158 | case FB_EVENT_MODE_DELETE: | 3186 | case FB_EVENT_MODE_DELETE: |
3159 | mode = event->data; | 3187 | mode = event->data; |
3188 | if (!lock_fb_info(info)) { | ||
3189 | ret = -ENODEV; | ||
3190 | goto done; | ||
3191 | } | ||
3160 | ret = fbcon_mode_deleted(info, mode); | 3192 | ret = fbcon_mode_deleted(info, mode); |
3193 | unlock_fb_info(info); | ||
3161 | break; | 3194 | break; |
3162 | case FB_EVENT_FB_UNBIND: | 3195 | case FB_EVENT_FB_UNBIND: |
3163 | ret = fbcon_fb_unbind(info->node); | 3196 | if (!lock_fb_info(info)) { |
3197 | ret = -ENODEV; | ||
3198 | goto done; | ||
3199 | } | ||
3200 | idx = info->node; | ||
3201 | unlock_fb_info(info); | ||
3202 | ret = fbcon_fb_unbind(idx); | ||
3164 | break; | 3203 | break; |
3165 | case FB_EVENT_FB_REGISTERED: | 3204 | case FB_EVENT_FB_REGISTERED: |
3166 | ret = fbcon_fb_registered(info); | 3205 | ret = fbcon_fb_registered(info); |
@@ -3178,17 +3217,31 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
3178 | con2fb->framebuffer = con2fb_map[con2fb->console - 1]; | 3217 | con2fb->framebuffer = con2fb_map[con2fb->console - 1]; |
3179 | break; | 3218 | break; |
3180 | case FB_EVENT_BLANK: | 3219 | case FB_EVENT_BLANK: |
3220 | if (!lock_fb_info(info)) { | ||
3221 | ret = -ENODEV; | ||
3222 | goto done; | ||
3223 | } | ||
3181 | fbcon_fb_blanked(info, *(int *)event->data); | 3224 | fbcon_fb_blanked(info, *(int *)event->data); |
3225 | unlock_fb_info(info); | ||
3182 | break; | 3226 | break; |
3183 | case FB_EVENT_NEW_MODELIST: | 3227 | case FB_EVENT_NEW_MODELIST: |
3228 | if (!lock_fb_info(info)) { | ||
3229 | ret = -ENODEV; | ||
3230 | goto done; | ||
3231 | } | ||
3184 | fbcon_new_modelist(info); | 3232 | fbcon_new_modelist(info); |
3233 | unlock_fb_info(info); | ||
3185 | break; | 3234 | break; |
3186 | case FB_EVENT_GET_REQ: | 3235 | case FB_EVENT_GET_REQ: |
3187 | caps = event->data; | 3236 | caps = event->data; |
3237 | if (!lock_fb_info(info)) { | ||
3238 | ret = -ENODEV; | ||
3239 | goto done; | ||
3240 | } | ||
3188 | fbcon_get_requirement(info, caps); | 3241 | fbcon_get_requirement(info, caps); |
3242 | unlock_fb_info(info); | ||
3189 | break; | 3243 | break; |
3190 | } | 3244 | } |
3191 | |||
3192 | done: | 3245 | done: |
3193 | return ret; | 3246 | return ret; |
3194 | } | 3247 | } |
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index cfd9dce1ce0b..b64f061dd447 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
@@ -1086,13 +1086,8 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1086 | return -EINVAL; | 1086 | return -EINVAL; |
1087 | con2fb.framebuffer = -1; | 1087 | con2fb.framebuffer = -1; |
1088 | event.data = &con2fb; | 1088 | event.data = &con2fb; |
1089 | |||
1090 | if (!lock_fb_info(info)) | ||
1091 | return -ENODEV; | ||
1092 | event.info = info; | 1089 | event.info = info; |
1093 | fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event); | 1090 | fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event); |
1094 | unlock_fb_info(info); | ||
1095 | |||
1096 | ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; | 1091 | ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; |
1097 | break; | 1092 | break; |
1098 | case FBIOPUT_CON2FBMAP: | 1093 | case FBIOPUT_CON2FBMAP: |
@@ -1109,12 +1104,8 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1109 | break; | 1104 | break; |
1110 | } | 1105 | } |
1111 | event.data = &con2fb; | 1106 | event.data = &con2fb; |
1112 | if (!lock_fb_info(info)) | ||
1113 | return -ENODEV; | ||
1114 | event.info = info; | 1107 | event.info = info; |
1115 | ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, | 1108 | ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); |
1116 | &event); | ||
1117 | unlock_fb_info(info); | ||
1118 | break; | 1109 | break; |
1119 | case FBIOBLANK: | 1110 | case FBIOBLANK: |
1120 | if (!lock_fb_info(info)) | 1111 | if (!lock_fb_info(info)) |