记一次misc_register注册失败

探讨了在内核log中遇到的misc_register注册失败警告,深入分析代码后发现是由于设备驱动程序被意外调用了两次,且存在一个括号错误导致野指针问题。最终定位到某CPU厂商代码中的bug并提出修复建议。

        在看内核log时,发现有一个misc_register注册失败的warning。一开始以为配置的参数有误,看代码也使用了参数MISC_DYNAMIC_MINOR,不太可能出现注册失败的情况。

int misc_register(struct miscdevice * misc)
{
	dev_t dev;
	int err = 0;
	bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);

	if (is_dynamic) {
		struct miscdevice *c;
		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
		if (i >= DYNAMIC_MINORS) {
			err = -EBUSY;
			goto out;
		}
		list_for_each_entry(c, &misc_list, list) {
			if (c == misc) {
				err = -EEXIST;
				goto out;
			}
		}
		misc->minor = DYNAMIC_MINORS - i - 1;
		set_bit(i, misc_minors);
	} else {
		
	    ...
	}

	...
}

加打印发现is_dynamic为0,但有使用参数MISC_DYNAMIC_MINOR,不应该为0。出问题的代码如下,能看出哪里有问题没

static int xxx_img_probe(struct platform_device *pdev)
{
	int ret = 0;

	ret = misc_register(&image_dev);
	if (ret) {
		pr_err("cannot register miscdev on minor=%d (%d).\n",IMAGE_MINOR, ret);
		r
请分析下面代码,帮我分析每个函数有什么功能,包括运行逻辑等进行详细分析 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/device.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/uaccess.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/gpio.h> #include <linux/delay.h> #include <linux/timer.h> #include <linux/of_gpio.h> static int nowayout = WATCHDOG_NOWAYOUT; static unsigned long wdt_status; static unsigned long boot_status; int wdt_reboot_time = 6000; /* 内核停止喂狗倒计时10分钟 */ int wdt__min_reboot_time = 600; /* 内核至少喂狗600*100ms:60秒 */ int wdt_timer_stop = 0; int disable_wdt_file = 0; /* 0表示文件,1表示不文件 */ #define WDT_IN_USE 0 #define WDT_OK_TO_CLOSE 1 #define WDT_ENABLED 2 #define WDT_REBOOT_TIME 50 /* 上层接管后50*100ms不喂狗系统则重启 */ #define WDT_HIGH_LEVEL 1 #define WDT_LOW_LEVEL 0 /* 喂狗寄存器 */ static uintptr_t g_wtd_reg_base_addr_mapped; void set_wdt_reg_val(unsigned int val) { if (g_wtd_reg_base_addr_mapped) { writel(val, (volatile void __iomem *)g_wtd_reg_base_addr_mapped); } } static unsigned long adm706_watchdog_timeout(void) { return (1600); // 1600 超时时间 } static void wdt_enable(void) { set_wdt_reg_val(WDT_HIGH_LEVEL); msleep(10); // 时间缓冲10ms set_wdt_reg_val(WDT_LOW_LEVEL); } /* returns 0 if the timer was successfully disabled */ static int wdt_disable(void) { printk(KERN_INFO "WATCHDOG: can't be Disabled by software\n"); return 0; } static int adm706_wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(WDT_IN_USE, &wdt_status)) return -EBUSY; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); wdt_enable(); set_bit(WDT_ENABLED, &wdt_status); return nonseekable_open(inode, file); } static ssize_t adm706_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) { if (len) { if (!nowayout) { size_t i; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); for (i = 0; i != len; i++) { char c; if (get_user(c, data + i)) return -EFAULT; if (c == 'V') set_bit(WDT_OK_TO_CLOSE, &wdt_status); } } wdt_reboot_time = WDT_REBOOT_TIME; } return len; } static const struct watchdog_info ident = { .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "adm706 watchdog", }; static long adm706_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int options; int ret = -ENOTTY; int __user *argp = (int __user *)arg; switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user(argp, &ident, sizeof ident)) { ret = -EFAULT; } else { ret = 0; } break; case WDIOC_GETSTATUS: ret = put_user(0, argp); break; case WDIOC_GETBOOTSTATUS: ret = put_user(boot_status, argp); break; case WDIOC_SETOPTIONS: options = arg; if (options & WDIOS_DISABLECARD) { if (!nowayout) { if (wdt_disable() == 0) { set_bit(WDT_OK_TO_CLOSE, &wdt_status); ret = 0; } else { ret = -ENXIO; } } else { ret = 0; } } if (options & WDIOS_ENABLECARD) { wdt_enable(); ret = 0; } break; case WDIOC_KEEPALIVE: wdt_enable(); ret = 0; break; case WDIOC_SETTIMEOUT: ret = _IOC_SIZE(cmd); break; case WDIOC_GETTIMEOUT: ret = put_user(adm706_watchdog_timeout(), argp); break; default: break; } return ret; } static int adm706_wdt_release(struct inode *inode, struct file *file) { int state = 1; if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { if (test_bit(WDT_ENABLED, &wdt_status)) { state = wdt_disable(); } } /* if the timer is not disbaled reload and notify that we are still * going down */ if (state != 0) { wdt_enable(); } clear_bit(WDT_IN_USE, &wdt_status); clear_bit(WDT_OK_TO_CLOSE, &wdt_status); printk(KERN_INFO "watchdog released, system is going to reboot ...\n"); return 0; } static const struct file_operations adm706_wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = adm706_wdt_write, .unlocked_ioctl = adm706_wdt_ioctl, .open = adm706_wdt_open, .release = adm706_wdt_release, }; static struct miscdevice adm706_wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &adm706_wdt_fops, }; struct task_struct *p_creat_file; static void kick_wdt_fn(struct timer_list *timer) { static unsigned int count = 0; set_wdt_reg_val(count & WDT_HIGH_LEVEL); count++; mod_timer(timer, jiffies + msecs_to_jiffies(100)); // 100 定时器每次递加时间为100s } DEFINE_TIMER(kick_wdt_timer, kick_wdt_fn); /* 内核中通用喂狗函数,可以放置在内核任何需要喂狗的地方 */ void adm706_wdt_common(void) { static int val = 0; val = (val ? WDT_LOW_LEVEL : WDT_HIGH_LEVEL); if (wdt_timer_stop == 0) { set_wdt_reg_val(val); } } EXPORT_SYMBOL(adm706_wdt_common); static void adm706_wdt_init(void) { wdt_timer_stop = 0; printk("kernel start kick dog...\n"); add_timer(&kick_wdt_timer); mod_timer(&kick_wdt_timer, jiffies); } /* 注意:hi1230平台读写寄存器,通过WDG_WDI 喂外狗 */ static int adm706_wdt_probe(struct platform_device *pdev) { int ret; struct resource *res = NULL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { printk("watchdog get res fail.\n"); return -ENXIO; } res = request_mem_region(res->start, resource_size(res), pdev->name); if (res == NULL) { printk("watchdog request mem fail.\n"); return -EBUSY; } g_wtd_reg_base_addr_mapped = (uintptr_t)ioremap(res->start, resource_size(res)); adm706_wdt_init(); ret = misc_register(&adm706_wdt_miscdev); if (ret == 0) { printk(KERN_INFO "adm706 watchdog timer: timeout 1.6 sec\n"); if (!nowayout) { printk("Watchdog can be stoped.\n"); } else { printk("Watchdog can not be stopped once started.\n"); } } return ret; } static int adm706_wdt_remove(struct platform_device *pdev) { int error = 0; misc_deregister(&adm706_wdt_miscdev); return error; } static const struct of_device_id adm706_wdt_match[] = { { .compatible = "analog,adm706", }, {}, }; MODULE_DEVICE_TABLE(of, adm706_wdt_match); static struct platform_driver adm706_wdt_driver = { .driver = { .name = "wdt-adm706", .owner = THIS_MODULE, .of_match_table = adm706_wdt_match, }, .probe = adm706_wdt_probe, .remove = adm706_wdt_remove, }; /* pcs may be needed to bring up other drivers */ static int __init adm706_wdt_init_driver(void) { return platform_driver_register(&adm706_wdt_driver); } subsys_initcall(adm706_wdt_init_driver); static void __exit adm706_wdt_exit_driver(void) { platform_driver_unregister(&adm706_wdt_driver); } module_exit(adm706_wdt_exit_driver); module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); MODULE_AUTHOR("Walter"); MODULE_DESCRIPTION("ADM706 watchdog timer driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
最新发布
11-26
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值