new_data += FONT_EXTRA_WORDS * sizeof(int);
FNTSIZE(new_data) = size;
- REFCOUNT(new_data) = 0; /* usage counter */
+ REFCOUNT(new_data) = 1; /* usage counter */
FNTSUM(new_data) = 0;
p = (unsigned char *)font_data_buf(new_data);
if (font_data[i] != FONT_DATA
&& font_data_size(font_data[i]) == size
&& !memcmp(font_data[i], new_data, size)) {
- kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
+ font_data_put(new_data);
/* current font is the same as the new one */
if (i == unit)
return 0;
new_data = font_data[i];
+ font_data_get(new_data);
break;
}
}
- /* old font is user font */
- if (font_data[unit] != FONT_DATA) {
- if (--REFCOUNT(font_data[unit]) == 0)
- kfree(font_data[unit] -
- FONT_EXTRA_WORDS * sizeof(int));
- }
- REFCOUNT(new_data)++;
+
+ font_data_put(font_data[unit]);
font_data[unit] = new_data;
return 0;
static int newport_set_def_font(int unit, struct console_font *op)
{
- if (font_data[unit] != FONT_DATA) {
- if (--REFCOUNT(font_data[unit]) == 0)
- kfree(font_data[unit] -
- FONT_EXTRA_WORDS * sizeof(int));
- font_data[unit] = FONT_DATA;
- }
+ font_data_put(font_data[unit]);
+ font_data[unit] = FONT_DATA;
+ font_data_get(font_data[unit]);
return 0;
}
vc->vc_font.charcount = font->charcount;
p->fontdata = font->data;
+ font_data_get(p->fontdata);
}
cols = FBCON_SWAP(par->rotate, info->var.xres, info->var.yres);
vc->vc_font.charcount = fvc->vc_font.charcount;
p->fontdata = t->fontdata;
- p->userfont = t->userfont;
-
- if (p->userfont)
- REFCOUNT(p->fontdata)++;
+ font_data_get(p->fontdata);
} else {
const struct font_desc *font = NULL;
vc->vc_font.charcount = font->charcount;
p->fontdata = font->data;
+ font_data_get(p->fontdata);
}
}
static void fbcon_free_font(struct fbcon_display *p)
{
- if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
- kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
- p->fontdata = NULL;
- p->userfont = 0;
+ if (p->fontdata) {
+ font_data_put(p->fontdata);
+ p->fontdata = NULL;
+ }
}
static void set_vc_hi_font(struct vc_data *vc, bool set);
vc->vc_font.height = (*default_mode)->vc_font.height;
vc->vc_font.charcount = (*default_mode)->vc_font.charcount;
p->fontdata = t->fontdata;
- p->userfont = t->userfont;
- if (p->userfont)
- REFCOUNT(p->fontdata)++;
+ font_data_get(p->fontdata);
}
var->activate = FB_ACTIVATE_NOW;
struct fb_var_screeninfo var = info->var;
int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
- if (p->userfont && font_data_size(p->fontdata)) {
+ if (font_data_size(p->fontdata)) {
unsigned int size = vc_font_size(&vc->vc_font);
/*
}
static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
- font_data_t *data, int userfont)
+ font_data_t *data)
{
struct fb_info *info = fbcon_info_from_console(vc->vc_num);
struct fbcon_par *par = info->fbcon_par;
struct fbcon_display *p = &fb_display[vc->vc_num];
- int resize, ret, old_userfont, old_width, old_height, old_charcount;
+ int resize, ret, old_width, old_height, old_charcount;
font_data_t *old_fontdata = p->fontdata;
const u8 *old_data = vc->vc_font.data;
+ font_data_get(data);
+
resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
p->fontdata = data;
vc->vc_font.data = font_data_buf(p->fontdata);
- old_userfont = p->userfont;
- if ((p->userfont = userfont))
- REFCOUNT(data)++;
old_width = vc->vc_font.width;
old_height = vc->vc_font.height;
update_screen(vc);
}
- if (old_userfont && (--REFCOUNT(old_fontdata) == 0))
- kfree(old_fontdata - FONT_EXTRA_WORDS * sizeof(int));
+ if (old_fontdata)
+ font_data_put(old_fontdata);
+
return 0;
err_out:
p->fontdata = old_fontdata;
vc->vc_font.data = old_data;
-
- if (userfont) {
- p->userfont = old_userfont;
- if (--REFCOUNT(data) == 0)
- kfree(data - FONT_EXTRA_WORDS * sizeof(int));
- }
-
vc->vc_font.width = old_width;
vc->vc_font.height = old_height;
vc->vc_font.charcount = old_charcount;
+ font_data_put(data);
+
return ret;
}
int w = font->width;
int h = font->height;
int size, alloc_size;
- int i, csum;
+ int i, csum, ret;
font_data_t *new_data;
- u8 *data = font->data;
+ const u8 *data = font->data;
int pitch = PITCH(font->width);
/* Is there a reason why fbconsole couldn't handle any charcount >256?
new_data += FONT_EXTRA_WORDS * sizeof(int);
FNTSIZE(new_data) = size;
- REFCOUNT(new_data) = 0; /* usage counter */
+ REFCOUNT(new_data) = 1; /* usage counter */
for (i=0; i< charcount; i++) {
memcpy((u8 *)new_data + i * h * pitch, data + i * vpitch * pitch, h * pitch);
}
for (i = first_fb_vc; i <= last_fb_vc; i++) {
struct vc_data *tmp = vc_cons[i].d;
- if (fb_display[i].userfont &&
- fb_display[i].fontdata &&
+ if (fb_display[i].fontdata &&
FNTSUM(fb_display[i].fontdata) == csum &&
font_data_size(fb_display[i].fontdata) == size &&
tmp->vc_font.width == w &&
!memcmp(fb_display[i].fontdata, new_data, size)) {
- kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
- new_data = (u8 *)fb_display[i].fontdata;
+ font_data_get(fb_display[i].fontdata);
+ font_data_put(new_data);
+ new_data = fb_display[i].fontdata;
break;
}
}
- return fbcon_do_set_font(vc, font->width, font->height, charcount, new_data, 1);
+ ret = fbcon_do_set_font(vc, font->width, font->height, charcount, new_data);
+ font_data_put(new_data);
+
+ return ret;
}
static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font,
font->width = f->width;
font->height = f->height;
- return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data, 0);
+ return fbcon_do_set_font(vc, f->width, f->height, f->charcount, f->data);
}
static u16 palette_red[16];
* for more details.
*/
+#include <linux/container_of.h>
+#include <linux/font.h>
#include <linux/module.h>
-#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/types.h>
+
#if defined(__mc68000__)
#include <asm/setup.h>
#endif
-#include <linux/font.h>
/*
* Helpers for font_data_t
*/
+static struct font_data *to_font_data_struct(font_data_t *fd)
+{
+ return container_of(fd, struct font_data, data[0]);
+}
+
+static bool font_data_is_internal(font_data_t *fd)
+{
+ return !REFCOUNT(fd); /* internal fonts have no reference counting */
+}
+
+static void font_data_free(font_data_t *fd)
+{
+ kfree(to_font_data_struct(fd));
+}
+
+/**
+ * font_data_get - Acquires a reference on font data
+ * @fd: Font data
+ *
+ * Font data from user space is reference counted. The helper
+ * font_data_get() increases the reference counter by one. Invoke
+ * font_data_put() to release the reference.
+ *
+ * Internal font data is located in read-only memory. In this case
+ * the helper returns success without modifying the counter field.
+ * It is still required to call font_data_put() on internal font data.
+ */
+void font_data_get(font_data_t *fd)
+{
+ if (font_data_is_internal(fd))
+ return; /* never ref static data */
+
+ if (WARN_ON(!REFCOUNT(fd)))
+ return; /* should never be 0 */
+ ++REFCOUNT(fd);
+}
+EXPORT_SYMBOL_GPL(font_data_get);
+
+/**
+ * font_data_put - Release a reference on font data
+ * @fd: Font data
+ *
+ * Font data from user space is reference counted. The helper
+ * font_data_put() decreases the reference counter by one. If this was
+ * the final reference, it frees the allocated memory.
+ *
+ * Internal font data is located in read-only memory. In this case
+ * the helper returns success without modifying the counter field.
+ *
+ * Returns:
+ * True if the font data's memory buffer has been freed, false otherwise.
+ */
+bool font_data_put(font_data_t *fd)
+{
+ unsigned int count;
+
+ if (font_data_is_internal(fd))
+ return false; /* never unref static data */
+
+ if (WARN_ON(!REFCOUNT(fd)))
+ return false; /* should never be 0 */
+
+ count = --REFCOUNT(fd);
+ if (!count)
+ font_data_free(fd);
+
+ return !count;
+}
+EXPORT_SYMBOL_GPL(font_data_put);
+
/**
* font_data_size - Return size of the font data in bytes
* @fd: Font data