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 | |
| 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')
| -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 | } |
