For USB-attached (or other hot-(un)pluggable) framebuffers the current fbdev infrastructure is not very helpful. Each such driver currently needs to perform the ref-counting on its own in .fbops.fb_open and .fbops.fb_release callbacks. This patch moves the ref-counting in fbdev infrastructure. (drivers have not been adjusted, all those releasing fb_info in .fbops.fb_destroy will not work -- patch for those will follow later on, all the others will continue to work fine) API-wise the following changes are done: - num_registered_fb and registered_fb variables are no more exported. New functions fb_get_registered() and fb_is_registered() replace them. The only know user of those was fbcon, thus the large diff on fbcon.c Note: the accesses to registered_fb and num_registered_fb look racy as there was not protection at all around them, potentially letting register_framebuffer() register two framebuffers on the same minor concurrently, fbcon access should have been safe by combination of its use of console_semaphore and reaction to events. In this patch I combined most of fbcon's accesses to registered_fb and num_registered_fb into fb_is_registered(), though I'm not sure if the num-check optimization is worth to keep or its check should be put into a separate function. - framebuffer_release() is mapped to fb_put() but will go away when converting drivers Reference count for fb_info can be increased with fb_get(fb_info) and later released with fb_put(fb_info). If you have concerns regarding the API changes, please let me know. Thanks, Bruno drivers/video/console/fbcon.c | 285 ++++++++++++++++++++++++++--------------- drivers/video/fbmem.c | 254 +++++++++++++++++++++++++++---------- drivers/video/fbsysfs.c | 10 +- include/linux/fb.h | 20 +++- 4 files changed, 396 insertions(+), 173 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 84f8423..011ef4c 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -211,14 +211,11 @@ static inline void fbcon_set_rotation(struct fb_info *info) static void fbcon_rotate(struct fb_info *info, u32 rotate) { struct fbcon_ops *ops= info->fbcon_par; - struct fb_info *fb_info; if (!ops || ops->currcon == -1) return; - fb_info = registered_fb[con2fb_map[ops->currcon]]; - - if (info == fb_info) { + if (fb_is_registered(info, con2fb_map[ops->currcon])) { struct display *p = &fb_display[ops->currcon]; if (rotate < 4) @@ -241,9 +238,11 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate) return; for (i = first_fb_vc; i <= last_fb_vc; i++) { + struct fb_info *fb_info; vc = vc_cons[i].d; - if (!vc || vc->vc_mode != KD_TEXT || - registered_fb[con2fb_map[i]] != info) + if (!vc || vc->vc_mode != KD_TEXT) + continue; + if (!fb_is_registered(info, con2fb_map[i])) continue; p = &fb_display[vc->vc_num]; @@ -380,7 +379,7 @@ static void fb_flashcursor(struct work_struct *work) vc = vc_cons[ops->currcon].d; if (!vc || !CON_IS_VISIBLE(vc) || - registered_fb[con2fb_map[vc->vc_num]] != info || + !fb_is_registered(info, con2fb_map[vc->vc_num]) || vc->vc_deccm != 1) { release_console_sem(); return; @@ -529,7 +528,7 @@ static int fbcon_takeover(int show_logo) { int err, i; - if (!num_registered_fb) + if (!fb_is_registered(NULL, -1)) return -ENODEV; if (!show_logo) @@ -793,11 +792,12 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, if (show_logo) { struct vc_data *fg_vc = vc_cons[fg_console].d; struct fb_info *fg_info = - registered_fb[con2fb_map[fg_console]]; + fb_get_registered(con2fb_map[fg_console]); fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols, fg_vc->vc_rows, fg_vc->vc_cols, fg_vc->vc_rows); + fb_put(fg_info); } update_screen(vc_cons[fg_console].d); @@ -816,23 +816,24 @@ static int set_con2fb_map(int unit, int newidx, int user) { struct vc_data *vc = vc_cons[unit].d; int oldidx = con2fb_map[unit]; - struct fb_info *info = registered_fb[newidx]; - struct fb_info *oldinfo = NULL; + struct fb_info *info, *oldinfo = NULL; int found, err = 0; if (oldidx == newidx) return 0; - if (!info || fbcon_has_exited) + info = fbcon_has_exited ? NULL : fb_get_registered(newidx); + if (!info) return -EINVAL; if (!err && !search_for_mapped_con()) { info_idx = newidx; + fb_put(info); return fbcon_takeover(0); } if (oldidx != -1) - oldinfo = registered_fb[oldidx]; + oldinfo = fb_get_registered(oldidx); found = search_fb_in_map(newidx); @@ -864,6 +865,8 @@ static int set_con2fb_map(int unit, int newidx, int user) info_idx = newidx; release_console_sem(); + fb_put(oldinfo); + fb_put(info); return err; } @@ -926,30 +929,34 @@ static const char *fbcon_startup(void) int rows, cols; /* - * If num_registered_fb is zero, this is a call for the dummy part. - * The frame buffer devices weren't initialized yet. + * If fb_is_registered(NULL, -1) returns zero, this is a call for the + * dummy part. The frame buffer devices weren't initialized yet. */ - if (!num_registered_fb || info_idx == -1) + if (!fb_is_registered(NULL, -1) || info_idx == -1) return display_desc; /* - * Instead of blindly using registered_fb[0], we use info_idx, set by + * Instead of blindly using minor 0, we use info_idx, set by * fb_console_init(); */ - info = registered_fb[info_idx]; + info = fb_get_registered(info_idx); if (!info) return NULL; owner = info->fbops->owner; - if (!try_module_get(owner)) + if (!try_module_get(owner)) { + fb_put(info); return NULL; + } if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { module_put(owner); + fb_put(info); return NULL; } ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); if (!ops) { module_put(owner); + fb_put(info); return NULL; } @@ -1012,12 +1019,13 @@ static const char *fbcon_startup(void) fbcon_add_cursor_timer(info); fbcon_has_exited = 0; + fb_put(info); return display_desc; } static void fbcon_init(struct vc_data *vc, int init) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops; struct vc_data **default_mode = vc->vc_display_fg; struct vc_data *svc = *default_mode; @@ -1026,7 +1034,7 @@ static void fbcon_init(struct vc_data *vc, int init) int cap, ret; if (info_idx == -1 || info == NULL) - return; + goto out; cap = info->flags; @@ -1035,7 +1043,7 @@ static void fbcon_init(struct vc_data *vc, int init) logo = 0; if (var_to_display(p, &info->var, info)) - return; + goto out; if (!info->fbcon_par) con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); @@ -1153,6 +1161,8 @@ static void fbcon_init(struct vc_data *vc, int init) } ops->p = &fb_display[fg_console]; +out: + fb_put(info); } static void fbcon_free_font(struct display *p) @@ -1166,7 +1176,7 @@ static void fbcon_free_font(struct display *p) static void fbcon_deinit(struct vc_data *vc) { struct display *p = &fb_display[vc->vc_num]; - struct fb_info *info; + struct fb_info *info = NULL; struct fbcon_ops *ops; int idx; @@ -1176,7 +1186,7 @@ static void fbcon_deinit(struct vc_data *vc) if (idx == -1) goto finished; - info = registered_fb[idx]; + info = fb_get_registered(idx); if (!info) goto finished; @@ -1192,6 +1202,7 @@ static void fbcon_deinit(struct vc_data *vc) ops->flags &= ~FBCON_FLAGS_INIT; finished: + fb_put(info); if (!con_is_bound(&fb_con)) fbcon_exit(); @@ -1226,17 +1237,17 @@ finished: static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, int width) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; u_int y_break; if (fbcon_is_inactive(vc, info)) - return; + goto out; if (!height || !width) - return; + goto out; if (sy < vc->vc_top && vc->vc_top == logo_lines) vc->vc_top = 0; @@ -1251,12 +1262,14 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, width); } else ops->clear(vc, info, real_y(p, sy), sx, height, width); +out: + fb_put(info); } static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct display *p = &fb_display[vc->vc_num]; struct fbcon_ops *ops = info->fbcon_par; @@ -1264,6 +1277,7 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, get_color(vc, info, scr_readw(s), 1), get_color(vc, info, scr_readw(s), 0)); + fb_put(info); } static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) @@ -1276,22 +1290,23 @@ static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; if (!fbcon_is_inactive(vc, info)) ops->clear_margins(vc, info, bottom_only); + fb_put(info); } static void fbcon_cursor(struct vc_data *vc, int mode) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; int y; int c = scr_readw((u16 *) vc->vc_pos); if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) - return; + goto out; if (vc->vc_cursor_type & 0x10) fbcon_del_cursor_timer(info); @@ -1311,6 +1326,8 @@ static void fbcon_cursor(struct vc_data *vc, int mode) ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); vbl_cursor_cnt = CURSOR_DRAW_DELAY; +out: + fb_put(info); } static int scrollback_phys_max = 0; @@ -1387,7 +1404,7 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, static __inline__ void ywrap_up(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; @@ -1402,11 +1419,12 @@ static __inline__ void ywrap_up(struct vc_data *vc, int count) if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; scrollback_current = 0; + fb_put(info); } static __inline__ void ywrap_down(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; @@ -1421,11 +1439,12 @@ static __inline__ void ywrap_down(struct vc_data *vc, int count) if (scrollback_max < 0) scrollback_max = 0; scrollback_current = 0; + fb_put(info); } static __inline__ void ypan_up(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct display *p = &fb_display[vc->vc_num]; struct fbcon_ops *ops = info->fbcon_par; @@ -1445,11 +1464,12 @@ static __inline__ void ypan_up(struct vc_data *vc, int count) if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; scrollback_current = 0; + fb_put(info); } static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; @@ -1469,11 +1489,12 @@ static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) if (scrollback_max > scrollback_phys_max) scrollback_max = scrollback_phys_max; scrollback_current = 0; + fb_put(info); } static __inline__ void ypan_down(struct vc_data *vc, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct display *p = &fb_display[vc->vc_num]; struct fbcon_ops *ops = info->fbcon_par; @@ -1493,11 +1514,12 @@ static __inline__ void ypan_down(struct vc_data *vc, int count) if (scrollback_max < 0) scrollback_max = 0; scrollback_current = 0; + fb_put(info); } static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; @@ -1517,6 +1539,7 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) if (scrollback_max < 0) scrollback_max = 0; scrollback_current = 0; + fb_put(info); } static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, @@ -1779,12 +1802,14 @@ static inline void fbcon_softback_note(struct vc_data *vc, int t, static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, int count) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - if (fbcon_is_inactive(vc, info)) + if (fbcon_is_inactive(vc, info)) { + fb_put(info); return -EINVAL; + } fbcon_cursor(vc, CM_ERASE); @@ -1812,6 +1837,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, (b - count)), vc->vc_video_erase_char, vc->vc_size_row * count); + fb_put(info); return 1; break; @@ -1884,6 +1910,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, (b - count)), vc->vc_video_erase_char, vc->vc_size_row * count); + fb_put(info); return 1; } break; @@ -1903,6 +1930,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, t), vc->vc_video_erase_char, vc->vc_size_row * count); + fb_put(info); return 1; break; @@ -1973,9 +2001,11 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, t), vc->vc_video_erase_char, vc->vc_size_row * count); + fb_put(info); return 1; } } + fb_put(info); return 0; } @@ -1983,14 +2013,14 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, int height, int width) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct display *p = &fb_display[vc->vc_num]; if (fbcon_is_inactive(vc, info)) - return; + goto out; if (!width || !height) - return; + goto out; /* Split blits that cross physical y_wrap case. * Pathological case involves 4 blits, better to use recursive @@ -2001,12 +2031,14 @@ static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, */ fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, p->vrows - p->yscroll); +out: + fb_put(info); } static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, int dy, int dx, int height, int width, u_int y_break) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; u_int b; @@ -2023,7 +2055,7 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, y_break); } - return; + goto out; } if (dy < y_break && dy + height > y_break) { @@ -2039,10 +2071,12 @@ static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int s fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, y_break); } - return; + goto out; } ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, height, width); +out: + fb_put(info); } static void updatescrollmode(struct display *p, @@ -2095,7 +2129,7 @@ static void updatescrollmode(struct display *p, static int fbcon_resize(struct vc_data *vc, unsigned int width, unsigned int height, unsigned int user) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; struct fb_var_screeninfo var = info->var; @@ -2118,12 +2152,12 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); mode = fb_find_best_mode(&var, &info->modelist); if (mode == NULL) - return -EINVAL; + goto einval; display_to_var(&var, p); fb_videomode_to_var(&var, mode); if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh) - return -EINVAL; + goto einval; DPRINTK("resize now %ix%i\n", var.xres, var.yres); if (CON_IS_VISIBLE(vc)) { @@ -2135,7 +2169,11 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width, ops->var = info->var; } updatescrollmode(p, info, vc); + fb_put(info); return 0; +einval: + fb_put(info); + return -EINVAL; } static int fbcon_switch(struct vc_data *vc) @@ -2146,7 +2184,7 @@ static int fbcon_switch(struct vc_data *vc) struct fb_var_screeninfo var; int i, ret, prev_console, charcnt = 256; - info = registered_fb[con2fb_map[vc->vc_num]]; + info = fb_get_registered(con2fb_map[vc->vc_num]); ops = info->fbcon_par; if (softback_top) { @@ -2168,7 +2206,7 @@ static int fbcon_switch(struct vc_data *vc) prev_console = ops->currcon; if (prev_console != -1) - old_info = registered_fb[con2fb_map[prev_console]]; + old_info = fb_get_registered(con2fb_map[prev_console]); /* * FIXME: If we have multiple fbdev's loaded, we need to * update all info->currcon. Perhaps, we can place this @@ -2178,11 +2216,13 @@ static int fbcon_switch(struct vc_data *vc) * info->currcon = vc->vc_num; */ for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { - struct fbcon_ops *o = registered_fb[i]->fbcon_par; + struct fb_info *fb = fb_get_registered(i); + if (fb != NULL && fb->fbcon_par) { + struct fbcon_ops *o = fb->fbcon_par; o->currcon = vc->vc_num; } + fb_put(fb); } memset(&var, 0, sizeof(struct fb_var_screeninfo)); display_to_var(&var, p); @@ -2272,8 +2312,12 @@ static int fbcon_switch(struct vc_data *vc) vc->vc_origin + vc->vc_size_row * vc->vc_top, vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2); + fb_put(info); + fb_put(old_info); return 0; } + fb_put(info); + fb_put(old_info); return 1; } @@ -2304,7 +2348,7 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; if (mode_switch) { @@ -2341,12 +2385,13 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) else fbcon_add_cursor_timer(info); + fb_put(info); return 0; } static int fbcon_debug_enter(struct vc_data *vc) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; ops->save_graphics = ops->graphics; @@ -2354,17 +2399,19 @@ static int fbcon_debug_enter(struct vc_data *vc) if (info->fbops->fb_debug_enter) info->fbops->fb_debug_enter(info); fbcon_set_palette(vc, color_table); + fb_put(info); return 0; } static int fbcon_debug_leave(struct vc_data *vc) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; ops->graphics = ops->save_graphics; if (info->fbops->fb_debug_leave) info->fbops->fb_debug_leave(info); + fb_put(info); return 0; } @@ -2422,7 +2469,7 @@ static int fbcon_get_font(struct vc_data *vc, struct console_font *font) static int fbcon_do_set_font(struct vc_data *vc, int w, int h, const u8 * data, int userfont) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); struct fbcon_ops *ops = info->fbcon_par; struct display *p = &fb_display[vc->vc_num]; int resize; @@ -2520,6 +2567,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, if (old_data && (--REFCOUNT(old_data) == 0)) kfree(old_data - FONT_EXTRA_WORDS * sizeof(int)); + fb_put(info); return 0; } @@ -2547,7 +2595,7 @@ static int fbcon_copy_font(struct vc_data *vc, int con) static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); unsigned charcount = font->charcount; int w = font->width; int h = font->height; @@ -2558,24 +2606,32 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne /* Is there a reason why fbconsole couldn't handle any charcount >256? * If not this check should be changed to charcount < 256 */ - if (charcount != 256 && charcount != 512) + if (charcount != 256 && charcount != 512) { + fb_put(info); return -EINVAL; + } /* Make sure drawing engine can handle the font */ if (!(info->pixmap.blit_x & (1 << (font->width - 1))) || - !(info->pixmap.blit_y & (1 << (font->height - 1)))) + !(info->pixmap.blit_y & (1 << (font->height - 1)))) { + fb_put(info); return -EINVAL; + } /* Make sure driver can handle the font length */ - if (fbcon_invalid_charcount(info, charcount)) + if (fbcon_invalid_charcount(info, charcount)) { + fb_put(info); return -EINVAL; + } size = h * pitch * charcount; new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER); - if (!new_data) + if (!new_data) { + fb_put(info); return -ENOMEM; + } new_data += FONT_EXTRA_WORDS * sizeof(int); FNTSIZE(new_data) = size; @@ -2605,22 +2661,26 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne break; } } + fb_put(info); return fbcon_do_set_font(vc, font->width, font->height, new_data, 1); } static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); const struct font_desc *f; if (!name) f = get_default_font(info->var.xres, info->var.yres, info->pixmap.blit_x, info->pixmap.blit_y); - else if (!(f = find_font(name))) + else if (!(f = find_font(name))) { + fb_put(info); return -ENOENT; + } font->width = f->width; font->height = f->height; + fb_put(info); return fbcon_do_set_font(vc, f->width, f->height, f->data, 0); } @@ -2634,15 +2694,19 @@ static struct fb_cmap palette_cmap = { static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) { - struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; - int i, j, k, depth; + struct fb_info *info = fb_get_registered(con2fb_map[vc->vc_num]); + int i, j, k, depth, ret; u8 val; - if (fbcon_is_inactive(vc, info)) - return -EINVAL; + if (fbcon_is_inactive(vc, info)) { + ret = -EINVAL; + goto out; + } - if (!CON_IS_VISIBLE(vc)) - return 0; + if (!CON_IS_VISIBLE(vc)) { + ret = 0; + goto out; + } depth = fb_get_color_depth(&info->var, &info->fix); if (depth > 3) { @@ -2664,7 +2728,10 @@ static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) } else fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); - return fb_set_cmap(&palette_cmap, info); + ret = fb_set_cmap(&palette_cmap, info); +out: + fb_put(info); + return ret; } static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) @@ -2747,16 +2814,16 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) static int fbcon_scrolldelta(struct vc_data *vc, int lines) { - struct fb_info *info = registered_fb[con2fb_map[fg_console]]; + struct fb_info *info = fb_get_registered(con2fb_map[fg_console]); struct fbcon_ops *ops = info->fbcon_par; struct display *disp = &fb_display[fg_console]; - int offset, limit, scrollback_old; + int offset, limit, scrollback_old, ret = 0; if (softback_top) { if (vc->vc_num != fg_console) - return 0; + goto out; if (vc->vc_mode != KD_TEXT || !lines) - return 0; + goto out; if (logo_shown >= 0) { struct vc_data *conp2 = vc_cons[logo_shown].d; @@ -2789,11 +2856,13 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); fbcon_redraw_softback(vc, disp, lines); fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); - return 0; + goto out; } - if (!scrollback_phys_max) - return -ENOSYS; + if (!scrollback_phys_max) { + ret = -ENOSYS; + goto out; + } scrollback_old = scrollback_current; scrollback_current -= lines; @@ -2802,10 +2871,10 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) else if (scrollback_current > scrollback_max) scrollback_current = scrollback_max; if (scrollback_current == scrollback_old) - return 0; + goto out; if (fbcon_is_inactive(vc, info)) - return 0; + goto out; fbcon_cursor(vc, CM_ERASE); @@ -2832,7 +2901,10 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines) if (!scrollback_current) fbcon_cursor(vc, CM_DRAW); - return 0; + +out: + fb_put(info); + return ret; } static int fbcon_set_origin(struct vc_data *vc) @@ -2878,7 +2950,7 @@ static void fbcon_modechanged(struct fb_info *info) return; vc = vc_cons[ops->currcon].d; if (vc->vc_mode != KD_TEXT || - registered_fb[con2fb_map[ops->currcon]] != info) + !fb_is_registered(info, con2fb_map[ops->currcon])) return; p = &fb_display[vc->vc_num]; @@ -2920,7 +2992,7 @@ static void fbcon_set_all_vcs(struct fb_info *info) for (i = first_fb_vc; i <= last_fb_vc; i++) { vc = vc_cons[i].d; if (!vc || vc->vc_mode != KD_TEXT || - registered_fb[con2fb_map[i]] != info) + !fb_is_registered(info, con2fb_map[i])) continue; if (CON_IS_VISIBLE(vc)) { @@ -2945,7 +3017,6 @@ static void fbcon_set_all_vcs(struct fb_info *info) static int fbcon_mode_deleted(struct fb_info *info, struct fb_videomode *mode) { - struct fb_info *fb_info; struct display *p; int i, j, found = 0; @@ -2954,8 +3025,7 @@ static int fbcon_mode_deleted(struct fb_info *info, j = con2fb_map[i]; if (j == -1) continue; - fb_info = registered_fb[j]; - if (fb_info != info) + if (!fb_is_registered(info, j)) continue; p = &fb_display[i]; if (!p || !p->mode) @@ -3028,7 +3098,9 @@ static int fbcon_fb_unregistered(struct fb_info *info) info_idx = -1; for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL) { + struct fb_info *fb_info = fb_get_registered(i); + fb_put(fb_info); + if (fb_info != NULL) { info_idx = i; break; } @@ -3045,7 +3117,7 @@ static int fbcon_fb_unregistered(struct fb_info *info) if (primary_device == idx) primary_device = -1; - if (!num_registered_fb) + if (!fb_is_registered(NULL, -1)) unregister_con_driver(&fb_con); return 0; @@ -3132,7 +3204,7 @@ static void fbcon_fb_blanked(struct fb_info *info, int blank) vc = vc_cons[ops->currcon].d; if (vc->vc_mode != KD_TEXT || - registered_fb[con2fb_map[ops->currcon]] != info) + !fb_is_registered(info, con2fb_map[ops->currcon])) return; if (CON_IS_VISIBLE(vc)) { @@ -3152,7 +3224,7 @@ static void fbcon_new_modelist(struct fb_info *info) const struct fb_videomode *mode; for (i = first_fb_vc; i <= last_fb_vc; i++) { - if (registered_fb[con2fb_map[i]] != info) + if (!fb_is_registered(info, con2fb_map[i])) continue; if (!fb_display[i].mode) continue; @@ -3324,12 +3396,13 @@ static ssize_t store_rotate(struct device *device, acquire_console_sem(); idx = con2fb_map[fg_console]; - if (idx == -1 || registered_fb[idx] == NULL) + info = fb_get_registered(idx); + if (!info); goto err; - info = registered_fb[idx]; rotate = simple_strtoul(buf, last, 0); fbcon_rotate(info, rotate); + fb_put(info); err: release_console_sem(); return count; @@ -3349,12 +3422,13 @@ static ssize_t store_rotate_all(struct device *device, acquire_console_sem(); idx = con2fb_map[fg_console]; - if (idx == -1 || registered_fb[idx] == NULL) + info = fb_get_registered(idx); + if (!info) goto err; - info = registered_fb[idx]; rotate = simple_strtoul(buf, last, 0); fbcon_rotate_all(info, rotate); + fb_put(info); err: release_console_sem(); return count; @@ -3372,11 +3446,12 @@ static ssize_t show_rotate(struct device *device, acquire_console_sem(); idx = con2fb_map[fg_console]; - if (idx == -1 || registered_fb[idx] == NULL) + info = fb_get_registered(idx); + if (!info) goto err; - info = registered_fb[idx]; rotate = fbcon_get_rotate(info); + fb_put(info); err: release_console_sem(); return snprintf(buf, PAGE_SIZE, "%d\n", rotate); @@ -3395,10 +3470,10 @@ static ssize_t show_cursor_blink(struct device *device, acquire_console_sem(); idx = con2fb_map[fg_console]; - if (idx == -1 || registered_fb[idx] == NULL) + info = fb_get_registered(idx); + if (!info) goto err; - info = registered_fb[idx]; ops = info->fbcon_par; if (!ops) @@ -3406,6 +3481,7 @@ static ssize_t show_cursor_blink(struct device *device, blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; err: + fb_put(info); release_console_sem(); return snprintf(buf, PAGE_SIZE, "%d\n", blink); } @@ -3424,11 +3500,10 @@ static ssize_t store_cursor_blink(struct device *device, acquire_console_sem(); idx = con2fb_map[fg_console]; - if (idx == -1 || registered_fb[idx] == NULL) + info = fb_get_registered(idx); + if (!info) goto err; - info = registered_fb[idx]; - if (!info->fbcon_par) goto err; @@ -3443,6 +3518,7 @@ static ssize_t store_cursor_blink(struct device *device, } err: + fb_put(info); release_console_sem(); return count; } @@ -3479,13 +3555,15 @@ static int fbcon_init_device(void) static void fbcon_start(void) { - if (num_registered_fb) { + if (fb_is_registered(NULL, -1)) { int i; acquire_console_sem(); for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL) { + struct fb_info *fb_info = fb_get_registered(i); + if (fb_info) { + fb_put(fb_info); info_idx = i; break; } @@ -3511,7 +3589,7 @@ static void fbcon_exit(void) int pending; mapped = 0; - info = registered_fb[i]; + info = fb_get_registered(i); if (info == NULL) continue; @@ -3542,6 +3620,7 @@ static void fbcon_exit(void) if (info->queue.func == fb_flashcursor) info->queue.func = NULL; } + fb_put(info); } fbcon_has_exited = 1; diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index b066475..eb89523 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -42,8 +42,60 @@ #define FBPIXMAPSIZE (1024 * 8) -struct fb_info *registered_fb[FB_MAX] __read_mostly; -int num_registered_fb __read_mostly; +static spinlock_t lock_registered_fb = SPIN_LOCK_UNLOCKED; +static struct fb_info *registered_fb[FB_MAX] __read_mostly; +static int num_registered_fb __read_mostly; + +/** + * fb_get_registered - get a reference to framebuffer registered + * at @fbidx. + * + * Note: returns pointer to registered framebuffer with incremented + * refcount. + */ +struct fb_info *fb_get_registered(int fbidx) +{ + unsigned long flags; + struct fb_info *ret; + + if (fbidx < 0 || fbidx >= FB_MAX) + return NULL; + spin_lock_irqsave(&lock_registered_fb, flags); + ret = registered_fb[fbidx]; + if (IS_ERR(ret)) + ret = NULL; + if (ret) + kref_get(&ret->refcount); + spin_unlock_irqrestore(&lock_registered_fb, flags); + return ret; +} + +/** + * fb_is_registered - check if a framebuffer is registered + * + * @fbidx: -1 to check at position fb_info->node, other + * make sure framebuffer is registered at mentioned + * index + * @fb_info: NULL to request count of registered framebuffers, + * otherwise framebuffer to verify is registered + */ +int fb_is_registered(struct fb_info *fb_info, int fbidx) +{ + unsigned long flags; + int ret; + + if (fbidx < -1 || fbidx >= FB_MAX) + return 0; + spin_lock_irqsave(&lock_registered_fb, flags); + if (fbidx >= 0) + ret = registered_fb[fbidx] == fb_info; + else if (!fb_info) + ret = num_registered_fb; + else + ret = registered_fb[fb_info->node] == fb_info; + spin_unlock_irqrestore(&lock_registered_fb, flags); + return ret; +} int lock_fb_info(struct fb_info *info) { @@ -663,10 +715,12 @@ static void fb_seq_stop(struct seq_file *m, void *v) static int fb_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; - struct fb_info *fi = registered_fb[i]; + struct fb_info *fi = fb_get_registered(i); - if (fi) + if (fi) { seq_printf(m, "%d %s\n", fi->node, fi->fix.id); + fb_put(fi); + } return 0; } @@ -696,28 +750,34 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) unsigned long p = *ppos; struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; + struct fb_info *info = fb_get_registered(fbidx); u32 *buffer, *dst; u32 __iomem *src; int c, i, cnt = 0, err = 0; unsigned long total_size; - if (!info || ! info->screen_base) - return -ENODEV; + if (!info || ! info->screen_base) { + err = -ENODEV; + goto out; + } + + if (info->state != FBINFO_STATE_RUNNING) { + err = -EPERM; + goto out; + } - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; + if (info->fbops->fb_read) { + cnt = info->fbops->fb_read(info, buf, count, ppos); + goto out; + } - if (info->fbops->fb_read) - return info->fbops->fb_read(info, buf, count, ppos); - total_size = info->screen_size; if (total_size == 0) total_size = info->fix.smem_len; if (p >= total_size) - return 0; + goto out; if (count >= total_size) count = total_size; @@ -727,8 +787,10 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + if (!buffer) { + err = -ENOMEM; + goto out; + } src = (u32 __iomem *) (info->screen_base + p); @@ -762,6 +824,9 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) kfree(buffer); +out: + if (info) + fb_put(info); return (err) ? err : cnt; } @@ -771,28 +836,36 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) unsigned long p = *ppos; struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; + struct fb_info *info = fb_get_registered(fbidx); u32 *buffer, *src; u32 __iomem *dst; int c, i, cnt = 0, err = 0; unsigned long total_size; - if (!info || !info->screen_base) - return -ENODEV; + if (!info || !info->screen_base) { + err = -ENODEV; + goto out; + } - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; + if (info->state != FBINFO_STATE_RUNNING) { + err = -EPERM; + goto out; + } - if (info->fbops->fb_write) - return info->fbops->fb_write(info, buf, count, ppos); + if (info->fbops->fb_write) { + cnt = info->fbops->fb_write(info, buf, count, ppos); + goto out; + } total_size = info->screen_size; if (total_size == 0) total_size = info->fix.smem_len; - if (p > total_size) - return -EFBIG; + if (p > total_size) { + err = -EFBIG; + goto out; + } if (count > total_size) { err = -EFBIG; @@ -808,8 +881,10 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, GFP_KERNEL); - if (!buffer) - return -ENOMEM; + if (!buffer) { + err = -ENOMEM; + goto out; + } dst = (u32 __iomem *) (info->screen_base + p); @@ -846,6 +921,9 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) kfree(buffer); +out: + if (info) + fb_put(info); return (cnt) ? cnt : err; } @@ -1121,6 +1199,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, return -EINVAL; if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) return -EINVAL; + /* FIXME: these should never hit as fb_ioctl() looks for info before calling us! */ if (!registered_fb[con2fb.framebuffer]) request_module("fb%d", con2fb.framebuffer); if (!registered_fb[con2fb.framebuffer]) { @@ -1161,9 +1240,15 @@ static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; + struct fb_info *info = fb_get_registered(fbidx); + long ret; + + if (!info) + return -ENODEV; - return do_fb_ioctl(info, cmd, arg); + ret = do_fb_ioctl(info, cmd, arg); + fb_put(info); + return ret; } #ifdef CONFIG_COMPAT @@ -1285,10 +1370,14 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, { struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; + struct fb_info *info = fb_get_registered(fbidx); + struct fb_ops *fb; long ret = -ENOIOCTLCMD; + if (!info) + return -ENODEV; + fb = info->fbops; + switch(cmd) { case FBIOGET_VSCREENINFO: case FBIOPUT_VSCREENINFO: @@ -1314,6 +1403,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, ret = fb->fb_compat_ioctl(info, cmd, arg); break; } + fb_put(info); return ret; } #endif @@ -1322,23 +1412,31 @@ static int fb_mmap(struct file *file, struct vm_area_struct * vma) { int fbidx = iminor(file->f_path.dentry->d_inode); - struct fb_info *info = registered_fb[fbidx]; - struct fb_ops *fb = info->fbops; + struct fb_info *info = fb_get_registered(fbidx); + struct fb_ops *fb; unsigned long off; unsigned long start; u32 len; + int ret = 0; - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - off = vma->vm_pgoff << PAGE_SHIFT; - if (!fb) + if (!info) return -ENODEV; + fb = info->fbops; + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { + ret = -EINVAL; + goto out; + } + off = vma->vm_pgoff << PAGE_SHIFT; + if (!fb) { + ret = -ENODEV; + goto out; + } mutex_lock(&info->mm_lock); if (fb->fb_mmap) { - int res; - res = fb->fb_mmap(info, vma); + ret = fb->fb_mmap(info, vma); mutex_unlock(&info->mm_lock); - return res; + goto out; } /* frame buffer memory */ @@ -1349,15 +1447,18 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) off -= len; if (info->var.accel_flags) { mutex_unlock(&info->mm_lock); - return -EINVAL; + ret = -EINVAL; + goto out; } start = info->fix.mmio_start; len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); } mutex_unlock(&info->mm_lock); start &= PAGE_MASK; - if ((vma->vm_end - vma->vm_start + off) > len) - return -EINVAL; + if ((vma->vm_end - vma->vm_start + off) > len) { + ret = -EINVAL; + goto out; + } off += start; vma->vm_pgoff = off >> PAGE_SHIFT; /* This is an IO map - tell maydump to skip this VMA */ @@ -1365,9 +1466,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma) vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); fb_pgprotect(file, vma, off); if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - return 0; + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } +out: + fb_put(info); + return ret; } static int @@ -1381,12 +1486,13 @@ __releases(&info->lock) if (fbidx >= FB_MAX) return -ENODEV; - info = registered_fb[fbidx]; - if (!info) + info = fb_get_registered(fbidx); + if (!info) { request_module("fb%d", fbidx); - info = registered_fb[fbidx]; - if (!info) - return -ENODEV; + info = fb_get_registered(fbidx); + if (!info) + return -ENODEV; + } mutex_lock(&info->lock); if (!try_module_get(info->fbops->owner)) { res = -ENODEV; @@ -1404,6 +1510,8 @@ __releases(&info->lock) #endif out: mutex_unlock(&info->lock); + if (res) + fb_put(info); return res; } @@ -1419,6 +1527,7 @@ __releases(&info->lock) info->fbops->fb_release(info,1); module_put(info->fbops->owner); mutex_unlock(&info->lock); + fb_put(info); return 0; } @@ -1513,22 +1622,25 @@ void remove_conflicting_framebuffers(struct apertures_struct *a, /* check all firmware fbs and kick off if the base addr overlaps */ for (i = 0 ; i < FB_MAX; i++) { struct apertures_struct *gen_aper; - if (!registered_fb[i]) + struct fb_info *info = fb_get_registered(i); + if (!info) continue; - if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE)) - continue; + if (!(info->flags & FBINFO_MISC_FIRMWARE)) + goto done; - gen_aper = registered_fb[i]->apertures; + gen_aper = info->apertures; if (fb_do_apertures_overlap(gen_aper, a) || (primary && gen_aper && gen_aper->count && gen_aper->ranges[0].base == VGA_FB_PHYS)) { printk(KERN_ERR "fb: conflicting fb hw usage " "%s vs %s - removing generic driver\n", - name, registered_fb[i]->fix.id); - unregister_framebuffer(registered_fb[i]); + name, info->fix.id); + unregister_framebuffer(info); } +done: + fb_put(info); } } EXPORT_SYMBOL(remove_conflicting_framebuffers); @@ -1549,9 +1661,7 @@ register_framebuffer(struct fb_info *fb_info) int i; struct fb_event event; struct fb_videomode mode; - - if (num_registered_fb == FB_MAX) - return -ENXIO; + unsigned long flags; if (fb_check_foreignness(fb_info)) return -ENOSYS; @@ -1559,11 +1669,19 @@ register_framebuffer(struct fb_info *fb_info) remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id, fb_is_primary_device(fb_info)); + spin_lock_irqsave(&lock_registered_fb, flags); + if (num_registered_fb == FB_MAX) { + spin_unlock_irqrestore(&lock_registered_fb, flags); + return -ENXIO; + } + num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i]) break; + registered_fb[i] = ERR_PTR(-EBUSY); fb_info->node = i; + spin_unlock_irqrestore(&lock_registered_fb, flags); mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); @@ -1599,7 +1717,9 @@ register_framebuffer(struct fb_info *fb_info) fb_var_to_videomode(&mode, &fb_info->var); fb_add_videomode(&mode, &fb_info->modelist); + spin_lock_irqsave(&lock_registered_fb, flags); registered_fb[i] = fb_info; + spin_unlock_irqrestore(&lock_registered_fb, flags); event.info = fb_info; if (!lock_fb_info(fb_info)) @@ -1632,12 +1752,15 @@ unregister_framebuffer(struct fb_info *fb_info) { struct fb_event event; int i, ret = 0; + unsigned long flags; i = fb_info->node; - if (!registered_fb[i]) { + spin_lock_irqsave(&lock_registered_fb, flags); + if (registered_fb[i] != fb_info) ret = -EINVAL; + spin_unlock_irqrestore(&lock_registered_fb, flags); + if (ret) goto done; - } if (!lock_fb_info(fb_info)) @@ -1655,16 +1778,17 @@ unregister_framebuffer(struct fb_info *fb_info) (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); + spin_lock_irqsave(&lock_registered_fb, flags); registered_fb[i]=NULL; num_registered_fb--; + spin_unlock_irqrestore(&lock_registered_fb, flags); fb_cleanup_device(fb_info); device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); /* this may free fb info */ - if (fb_info->fbops->fb_destroy) - fb_info->fbops->fb_destroy(fb_info); + fb_put(fb_info); done: return ret; } @@ -1864,10 +1988,10 @@ __setup("video=", video_setup); * Visible symbols for modules */ +EXPORT_SYMBOL(fb_get_registered); +EXPORT_SYMBOL(fb_is_registered); EXPORT_SYMBOL(register_framebuffer); EXPORT_SYMBOL(unregister_framebuffer); -EXPORT_SYMBOL(num_registered_fb); -EXPORT_SYMBOL(registered_fb); EXPORT_SYMBOL(fb_show_logo); EXPORT_SYMBOL(fb_set_var); EXPORT_SYMBOL(fb_blank); diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 0a08f13..be5f342 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -58,6 +58,7 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) info->par = p + fb_info_size; info->device = dev; + kref_init(&info->refcount); #ifdef CONFIG_FB_BACKLIGHT mutex_init(&info->bl_curve_mutex); @@ -78,12 +79,17 @@ EXPORT_SYMBOL(framebuffer_alloc); * framebuffer info structure. * */ -void framebuffer_release(struct fb_info *info) +void _fb_destroy(struct kref *ref) { + struct fb_info *info = container_of(ref, struct fb_info, refcount); + + if (info->fbops->fb_destroy) + info->fbops->fb_destroy(info); + kfree(info->apertures); kfree(info); } -EXPORT_SYMBOL(framebuffer_release); +EXPORT_SYMBOL(_fb_destroy); static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { diff --git a/include/linux/fb.h b/include/linux/fb.h index f0268de..9d59684 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -834,6 +834,7 @@ struct fb_info { int flags; struct mutex lock; /* Lock for open/release/ioctl funcs */ struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */ + struct kref refcount; /* Reference count of framebuffer */ struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ struct fb_monspecs monspecs; /* Current Monitor specs */ @@ -986,6 +987,8 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos); /* drivers/video/fbmem.c */ +extern struct fb_info *fb_get_registered(int fbidx); +extern int fb_is_registered(struct fb_info *fb_info, int fbidx); extern int register_framebuffer(struct fb_info *fb_info); extern int unregister_framebuffer(struct fb_info *fb_info); extern void remove_conflicting_framebuffers(struct apertures_struct *a, @@ -1002,8 +1005,8 @@ extern int fb_get_color_depth(struct fb_var_screeninfo *var, extern int fb_get_options(char *name, char **option); extern int fb_new_modelist(struct fb_info *info); -extern struct fb_info *registered_fb[FB_MAX]; -extern int num_registered_fb; +/* extern struct fb_info *registered_fb[FB_MAX]; +extern int num_registered_fb; */ extern struct class *fb_class; extern int lock_fb_info(struct fb_info *info); @@ -1057,11 +1060,22 @@ static inline bool fb_be_math(struct fb_info *info) /* drivers/video/fbsysfs.c */ extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); -extern void framebuffer_release(struct fb_info *info); +#define framebuffer_release(a) fb_put(a) +extern void _fb_destroy(struct kref *ref); extern int fb_init_device(struct fb_info *fb_info); extern void fb_cleanup_device(struct fb_info *head); extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max); +static inline void fb_get(struct fb_info *fb_info) +{ + kref_get(&fb_info->refcount); +} +static inline void fb_put(struct fb_info *fb_info) +{ + if (fb_info) + kref_put(&fb_info->refcount, _fb_destroy); +} + /* drivers/video/fbmon.c */ #define FB_MAXTIMINGS 0 #define FB_VSYNCTIMINGS 1 -- To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html