Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

viafb: fix rmmod bug

This fixes a bug caused by changing pointers (viafb_mode, viafb_mode1)
assigned by module_param. It reduces driver complexity by not needlessly
changing these vars as they are only read once and removing now
superfluous code.

On unpatched kernels loading viafb with viafb_mode or viafb_mode1 option
used and afterwards unloading it results in:

kernel BUG at mm/slub.c:2926!
invalid opcode: 0000 [#1] PREEMPT
last sysfs file: /sys/devices/virtual/block/loop0/removable
Modules linked in: snd_hda_codec_realtek snd_hda_intel snd_hda_codec
snd_hwdep snd_pcm rtl8187 snd_timer eeprom_93cx6 mmc_block snd soundcore
via_sdmmc fb snd_page_alloc i2c_algo_bit i2c_viapro ehci_hcd uhci_hcd
cfbcopyarea mmc_core cfbimgblt cfbfillrect video output [last unloaded:
viafb]

Pid: 3355, comm: rmmod Not tainted (2.6.31-rc1 #0)
EIP: 0060:[<c106a759>] EFLAGS: 00010246 CPU: 0
EIP is at kfree+0x80/0xda
EAX: c17c2da0 EBX: dc7edbdc ECX: 0000010f EDX: 00000000
ESI: c102c700 EDI: dc7ed8fa EBP: d703ff2c ESP: d703ff20
DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068
Process rmmod (pid: 3355, ti=d703e000 task=db1412c0 task.ti=d703e000)
Stack:
dc7edbdc 00000014 00000016 d703ff40 c102c700 dc7f45d4 dc7f45d4 00000880
d703ff4c c103e571 00000000 d703ffac c103e751 66616976 da140062 db89ba80
00000328 d702edf8 db89ba80 d703ff9c c105d0f0 00000200 da14f898 00000014
Call Trace:
[<c102c700>] ? destroy_params+0x1e/0x2b
[<c103e571>] ? free_module+0xa2/0xd7
[<c103e751>] ? sys_delete_module+0x1ab/0x1da
[<c105d0f0>] ? do_munmap+0x20a/0x225
[<c10029b4>] ? sysenter_do_call+0x12/0x26
Code: 10 76 7a 8d 87 00 00 00 40 c1 e8 0c c1 e0 05 03 05 1c 87 41 c1 66 83 38 00 79 03 8b 40 0c 8b 10 84 d2 78 12 66 f7 c2 00 c0 75 04 <0f> 0b eb fe e8 6f 5a fe ff eb 47 8b 55 04 8b 58 0c 9c 5e fa 3b
EIP: [<c106a759>] kfree+0x80/0xda SS:ESP 0068:d703ff20

This is caused by the current code changing the pointers assigned by
module_param. During unload it tries to free the memory the pointers
point at which is now part of an internal structure.

The patch simply avoids changing the pointers. This is okay as they are
read only once during the initialization process.

Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Cc: Scott Fang <ScottFang@viatech.com.cn>
Cc: Joseph Chan <JosephChan@via.com.tw>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Florian Tobias Schandinat and committed by
Linus Torvalds
52159444 4bfc4495

+53 -70
+2 -2
drivers/video/via/hw.c
··· 2407 2407 viafb_dvi_set_mode(viafb_get_mode_index 2408 2408 (viaparinfo->tmds_setting_info->h_active, 2409 2409 viaparinfo->tmds_setting_info-> 2410 - v_active, 1), 2410 + v_active), 2411 2411 video_bpp1, viaparinfo-> 2412 2412 tmds_setting_info->iga_path); 2413 2413 } else { 2414 2414 viafb_dvi_set_mode(viafb_get_mode_index 2415 2415 (viaparinfo->tmds_setting_info->h_active, 2416 2416 viaparinfo-> 2417 - tmds_setting_info->v_active, 0), 2417 + tmds_setting_info->v_active), 2418 2418 video_bpp, viaparinfo-> 2419 2419 tmds_setting_info->iga_path); 2420 2420 }
+3 -12
drivers/video/via/lcd.c
··· 580 580 int reg_num = 0; 581 581 struct io_reg *lcd_patch_reg = NULL; 582 582 583 - if (viaparinfo->lvds_setting_info->iga_path == IGA2) 584 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 1); 585 - else 586 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 0); 583 + vmode_index = viafb_get_mode_index(set_hres, set_vres); 587 584 switch (panel_id) { 588 585 /* LCD 800x600 */ 589 586 case LCD_PANEL_ID1_800X600: ··· 758 761 int reg_num = 0; 759 762 struct io_reg *lcd_patch_reg = NULL; 760 763 761 - if (viaparinfo->lvds_setting_info->iga_path == IGA2) 762 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 1); 763 - else 764 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 0); 764 + vmode_index = viafb_get_mode_index(set_hres, set_vres); 765 765 766 766 switch (panel_id) { 767 767 case LCD_PANEL_ID5_1400X1050: ··· 826 832 { 827 833 int vmode_index; 828 834 829 - if (viaparinfo->lvds_setting_info->iga_path == IGA2) 830 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 1); 831 - else 832 - vmode_index = viafb_get_mode_index(set_hres, set_vres, 0); 835 + vmode_index = viafb_get_mode_index(set_hres, set_vres); 833 836 834 837 viafb_unlock_crt(); 835 838
+47 -54
drivers/video/via/viafbdev.c
··· 32 32 /* video mode */ 33 33 static char *viafb_mode = "640x480"; 34 34 static char *viafb_mode1 = "640x480"; 35 - static int viafb_resMode = VIA_RES_640X480; 36 35 37 36 /* Added for specifying active devices.*/ 38 37 char *viafb_active_dev = ""; ··· 55 56 56 57 /* Mode information */ 57 58 static const struct viafb_modeinfo viafb_modentry[] = { 58 - {480, 640, VIA_RES_480X640, "480x640"}, 59 - {640, 480, VIA_RES_640X480, "640x480"}, 60 - {800, 480, VIA_RES_800X480, "800x480"}, 61 - {800, 600, VIA_RES_800X600, "800x600"}, 62 - {1024, 768, VIA_RES_1024X768, "1024x768"}, 63 - {1152, 864, VIA_RES_1152X864, "1152x864"}, 64 - {1280, 1024, VIA_RES_1280X1024, "1280x1024"}, 65 - {1600, 1200, VIA_RES_1600X1200, "1600x1200"}, 66 - {1440, 1050, VIA_RES_1440X1050, "1440x1050"}, 67 - {1280, 768, VIA_RES_1280X768, "1280x768"}, 68 - {1280, 800, VIA_RES_1280X800, "1280x800"}, 69 - {1280, 960, VIA_RES_1280X960, "1280x960"}, 70 - {1920, 1440, VIA_RES_1920X1440, "1920x1440"}, 71 - {848, 480, VIA_RES_848X480, "848x480"}, 72 - {1400, 1050, VIA_RES_1400X1050, "1400x1050"}, 73 - {720, 480, VIA_RES_720X480, "720x480"}, 74 - {720, 576, VIA_RES_720X576, "720x576"}, 75 - {1024, 512, VIA_RES_1024X512, "1024x512"}, 76 - {1024, 576, VIA_RES_1024X576, "1024x576"}, 77 - {1024, 600, VIA_RES_1024X600, "1024x600"}, 78 - {1280, 720, VIA_RES_1280X720, "1280x720"}, 79 - {1920, 1080, VIA_RES_1920X1080, "1920x1080"}, 80 - {1366, 768, VIA_RES_1368X768, "1368x768"}, 81 - {1680, 1050, VIA_RES_1680X1050, "1680x1050"}, 82 - {960, 600, VIA_RES_960X600, "960x600"}, 83 - {1000, 600, VIA_RES_1000X600, "1000x600"}, 84 - {1024, 576, VIA_RES_1024X576, "1024x576"}, 85 - {1024, 600, VIA_RES_1024X600, "1024x600"}, 86 - {1088, 612, VIA_RES_1088X612, "1088x612"}, 87 - {1152, 720, VIA_RES_1152X720, "1152x720"}, 88 - {1200, 720, VIA_RES_1200X720, "1200x720"}, 89 - {1280, 600, VIA_RES_1280X600, "1280x600"}, 90 - {1360, 768, VIA_RES_1360X768, "1360x768"}, 91 - {1440, 900, VIA_RES_1440X900, "1440x900"}, 92 - {1600, 900, VIA_RES_1600X900, "1600x900"}, 93 - {1600, 1024, VIA_RES_1600X1024, "1600x1024"}, 94 - {1792, 1344, VIA_RES_1792X1344, "1792x1344"}, 95 - {1856, 1392, VIA_RES_1856X1392, "1856x1392"}, 96 - {1920, 1200, VIA_RES_1920X1200, "1920x1200"}, 97 - {2048, 1536, VIA_RES_2048X1536, "2048x1536"}, 98 - {0, 0, VIA_RES_INVALID, "640x480"} 59 + {480, 640, VIA_RES_480X640}, 60 + {640, 480, VIA_RES_640X480}, 61 + {800, 480, VIA_RES_800X480}, 62 + {800, 600, VIA_RES_800X600}, 63 + {1024, 768, VIA_RES_1024X768}, 64 + {1152, 864, VIA_RES_1152X864}, 65 + {1280, 1024, VIA_RES_1280X1024}, 66 + {1600, 1200, VIA_RES_1600X1200}, 67 + {1440, 1050, VIA_RES_1440X1050}, 68 + {1280, 768, VIA_RES_1280X768,}, 69 + {1280, 800, VIA_RES_1280X800}, 70 + {1280, 960, VIA_RES_1280X960}, 71 + {1920, 1440, VIA_RES_1920X1440}, 72 + {848, 480, VIA_RES_848X480}, 73 + {1400, 1050, VIA_RES_1400X1050}, 74 + {720, 480, VIA_RES_720X480}, 75 + {720, 576, VIA_RES_720X576}, 76 + {1024, 512, VIA_RES_1024X512}, 77 + {1024, 576, VIA_RES_1024X576}, 78 + {1024, 600, VIA_RES_1024X600}, 79 + {1280, 720, VIA_RES_1280X720}, 80 + {1920, 1080, VIA_RES_1920X1080}, 81 + {1366, 768, VIA_RES_1368X768}, 82 + {1680, 1050, VIA_RES_1680X1050}, 83 + {960, 600, VIA_RES_960X600}, 84 + {1000, 600, VIA_RES_1000X600}, 85 + {1024, 576, VIA_RES_1024X576}, 86 + {1024, 600, VIA_RES_1024X600}, 87 + {1088, 612, VIA_RES_1088X612}, 88 + {1152, 720, VIA_RES_1152X720}, 89 + {1200, 720, VIA_RES_1200X720}, 90 + {1280, 600, VIA_RES_1280X600}, 91 + {1360, 768, VIA_RES_1360X768}, 92 + {1440, 900, VIA_RES_1440X900}, 93 + {1600, 900, VIA_RES_1600X900}, 94 + {1600, 1024, VIA_RES_1600X1024}, 95 + {1792, 1344, VIA_RES_1792X1344}, 96 + {1856, 1392, VIA_RES_1856X1392}, 97 + {1920, 1200, VIA_RES_1920X1200}, 98 + {2048, 1536, VIA_RES_2048X1536}, 99 + {0, 0, VIA_RES_INVALID} 99 100 }; 100 101 101 102 static struct fb_ops viafb_ops; ··· 176 177 if (var->vmode & FB_VMODE_INTERLACED || var->vmode & FB_VMODE_DOUBLE) 177 178 return -EINVAL; 178 179 179 - vmode_index = viafb_get_mode_index(var->xres, var->yres, 0); 180 + vmode_index = viafb_get_mode_index(var->xres, var->yres); 180 181 if (vmode_index == VIA_RES_INVALID) { 181 182 DEBUG_MSG(KERN_INFO 182 183 "viafb: Mode %dx%dx%d not supported!!\n", ··· 232 233 viafb_update_device_setting(info->var.xres, info->var.yres, 233 234 info->var.bits_per_pixel, viafb_refresh, 0); 234 235 235 - vmode_index = viafb_get_mode_index(info->var.xres, info->var.yres, 0); 236 + vmode_index = viafb_get_mode_index(info->var.xres, info->var.yres); 236 237 237 238 if (viafb_SAMM_ON == 1) { 238 239 DEBUG_MSG(KERN_INFO 239 240 "viafb_second_xres = %d, viafb_second_yres = %d, bpp = %d\n", 240 241 viafb_second_xres, viafb_second_yres, viafb_bpp1); 241 242 vmode_index1 = viafb_get_mode_index(viafb_second_xres, 242 - viafb_second_yres, 1); 243 + viafb_second_yres); 243 244 DEBUG_MSG(KERN_INFO "->viafb_SAMM_ON: index=%d\n", 244 245 vmode_index1); 245 246 ··· 1261 1262 return 0; 1262 1263 } 1263 1264 1264 - int viafb_get_mode_index(int hres, int vres, int flag) 1265 + int viafb_get_mode_index(int hres, int vres) 1265 1266 { 1266 1267 u32 i; 1267 1268 DEBUG_MSG(KERN_INFO "viafb_get_mode_index!\n"); ··· 1271 1272 viafb_modentry[i].yres == vres) 1272 1273 break; 1273 1274 1274 - viafb_resMode = viafb_modentry[i].mode_index; 1275 - if (flag) 1276 - viafb_mode1 = viafb_modentry[i].mode_res; 1277 - else 1278 - viafb_mode = viafb_modentry[i].mode_res; 1279 - 1280 - return viafb_resMode; 1275 + return viafb_modentry[i].mode_index; 1281 1276 } 1282 1277 1283 1278 static void check_available_device_to_enable(int device_id) ··· 2192 2199 strict_strtoul(tmpc, 0, &default_xres); 2193 2200 strict_strtoul(tmpm, 0, &default_yres); 2194 2201 2195 - vmode_index = viafb_get_mode_index(default_xres, default_yres, 0); 2202 + vmode_index = viafb_get_mode_index(default_xres, default_yres); 2196 2203 DEBUG_MSG(KERN_INFO "0->index=%d\n", vmode_index); 2197 2204 2198 2205 if (viafb_SAMM_ON == 1) {
+1 -2
drivers/video/via/viafbdev.h
··· 81 81 u32 xres; 82 82 u32 yres; 83 83 int mode_index; 84 - char *mode_res; 85 84 }; 86 85 extern unsigned int viafb_second_virtual_yres; 87 86 extern unsigned int viafb_second_virtual_xres; ··· 101 102 void viafb_memory_pitch_patch(struct fb_info *info); 102 103 void viafb_fill_var_timing_info(struct fb_var_screeninfo *var, int refresh, 103 104 int mode_index); 104 - int viafb_get_mode_index(int hres, int vres, int flag); 105 + int viafb_get_mode_index(int hres, int vres); 105 106 u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information 106 107 *plvds_setting_info, struct lvds_chip_information 107 108 *plvds_chip_info, u8 index);