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/video/console/fbcon.c | |
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/video/console/fbcon.c')
-rw-r--r-- | drivers/video/console/fbcon.c | 73 |
1 files changed, 63 insertions, 10 deletions
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 | } |