[理解] 为什么我的spin_lock_irqsave()没有锁住时钟中断?

本文通过编写内核模块和用户空间程序验证spin_lock_irqsave()函数的行为。实验结果显示该函数确实能够关闭中断,但在返回用户态时中断又被重新启用。

LZ:

 

尝试用spin_lock_irqsave(),发现没有禁掉时钟中断,不知道我哪里理解错了?
kernel版本:

  1. uname -a 结果:
  2. Linux localhost.localdomain 2.6.18 #1 Sat Jul 19 13:06:00 EDT 2008 i686 i686 i386 GNU/Linux
复制代码



我的代码:

  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/spinlock.h>
  5. static spinlock_t my_spinlock;
  6. static int __init init_test()
  7. {
  8.         unsigned long flags;
  9.         spin_lock_init(&my_spinlock);
  10.         spin_lock_irqsave(&my_spinlock, flags);
  11.         return 0;
  12. }
  13. static void __exit init_exit()
  14. {
  15.         return;
  16. }
  17. module_init(init_test);
  18. module_exit(init_exit);
复制代码



加载module以后,通过这样的命令查看墙上时钟:

  1. #date; sleep 2; date
  2. 输出结果:
  3. Sun Apr 19 12:59:42 EDT 2009
  4. Sun Apr 19 12:59:44 EDT 2009
复制代码


墙上时钟仍然在变化,说明时钟中断没有被禁掉。
但我理解的spin_lock_irqsave()会通过cli关掉所有IRQ,并且我在module中也没有spin_unlock_irqrestore(),所以按理说在加载了这个module之后,系统就不会响应任何外部可屏蔽中断了。但实际结果却不是这样。

我哪里理解错了呢?请各位指点,多谢!

 


 

解答:

写了一段kernel module和 userspace program来验证:

kernel module: my_spin_lock.c

  1. #include <linux/kernel.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/spinlock.h>
  5. #define IF_MASK 0x00000200
  6. static spinlock_t my_spinlock;
  7. static unsigned int is_interrupt_enable()
  8. {
  9.         unsigned long my_eflags;
  10.         asm volatile ("pushfl /n/t"
  11.         "popl %0"
  12.         :"=a"(my_eflags));
  13.         return (((my_eflags & IF_MASK) == 0) ? 0 : 1);
  14. }
  15. static int __init init_test()
  16. {
  17.         unsigned long flags;
  18.         spin_lock_init(&my_spinlock);
  19.         spin_lock_irqsave(&my_spinlock, flags);
  20.         printk("from kernelspace init: interrupt was enable : %d/n", is_interrupt_enable());
  21.         return 0;
  22. }
  23. static void __exit init_exit()
  24. {
  25.         return;
  26. }
  27. MODULE_LICENSE("GPL");
  28. module_init(init_test);
  29. module_exit(init_exit);
复制代码



编译以后生成my_spin_lock.ko

然后再写一段userspace program : test.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define IF_MASK 0x00000200
  4. #define CMD_LEN 100
  5. unsigned int is_interrupt_enable()
  6. {
  7.         unsigned long my_eflags;
  8.         asm volatile ("pushfl /n/t"
  9.         "popl %0 /n/t"
  10.         :"=a"(my_eflags));
  11.        
  12.         return (((my_eflags & IF_MASK) == 0) ? 0 : 1);
  13. }
  14. int main (int argc, char *argv[])
  15. {
  16.         if (argc != 2)
  17.         {
  18.                 printf("usersage: insmod your_kernel_module/n");
  19.                 return 0;       
  20.         }
  21.         char cmd[CMD_LEN];
  22.         memset(cmd, 0, sizeof(cmd));
  23.         sprintf(cmd, "insmod %s", argv[1]);
  24.         printf("from userspace, before insmod, inerrput is enable : %d/n", is_interrupt_enable());
  25.         system(cmd); /*insmod kernel module*/
  26.         printf("from userspace, after insmod, interrupt is enable : %d/n", is_interrupt_enable());       
  27.         memset(cmd, 0, sizeof(cmd));
  28.         sprintf(cmd, "rmmod %s", argv[1]);
  29.         system(cmd); /*rmmod kernel module*/
  30.        
  31.        
  32.         return 0;
  33. }
复制代码



编译以后生成test二进制文件

然后下命令:

  1. ./test my_spin_lock.ko
复制代码



运行结果:

QUOTE:
from userspace, before insmod, interrupt is enable : 1
from kernelspace init: interrupt was enable : 0
from userspace, after insmod, interrupt is enable: 1




可见,运行了kernel module的init函数以后,interrupt确实被禁止了。然后返回到用户态,interrupt被打开了。

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/ktime.h> #include <linux/reboot.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/clk.h> #include <linux/spinlock.h> #include <linux/timer.h> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt // 看门狗寄存器定义 #define INTERNAL_DOG_BASE_ADDR 0xaaaa0000 #define INTERNAL_DOG_OFFSET 0x1234 #define FEED_DOG_BASE_ADDR 0x1a000 #define FEED_DOG_OFFSET 0x011 #define WCV_L_OFFSET 0x00042 #define WCV_H_OFFSET 0x00023 #define WOR_OFFSET 0x00065 #define WRR_OFFSET 0x10011 #define WCS_REG_OFFSET 0x0 // 看门狗状态位 #define WDT_WS0 (1 << 0) #define WDT_WS1 (1 << 1) // 看门狗使能寄存器 static unsigned long wdt_is_open; static char expect_close; static int heartbeat = 60; module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default=60)"); static bool nowayout = false; module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=0)"); struct wdt_device { struct watchdog_device wdd; struct device *dev; void __iomem *base; struct clk *clk; int irq; unsigned long status; bool nowayout; u32 wdog_offset_value; u64 last_feed_time; u64 timeout_ns; u64 last_feed; u32 wcs_value; u32 wor_value; spinlock_t lock; // 添加自旋锁保护共享资源 }; static int wdog_start(struct watchdog_device *wdd) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); iowrite32(0x1, wd->base + INTERNAL_DOG_OFFSET); return 0; } static int wdog_stop(struct watchdog_device *wdd) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); iowrite32(0x0, wd->base + INTERNAL_DOG_OFFSET); return 0; } static int wdog_feed(struct watchdog_device *wdd) { u64 current_time; u64 new_compare_value; unsigned long flags; struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); spin_lock_irqsave(&wd->lock, flags); iowrite32(0x1, wd->base + FEED_DOG_OFFSET); current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); return 0; } static u32 read_WRR(struct wdt_device *wd) { u32 wrr_value; u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); wrr_value = ioread32(wd->base + WRR_OFFSET); current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); return wrr_value; } static void write_WOR(struct wdt_device *wd, u32 value) { u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); iowrite32(value, wd->base + WOR_OFFSET); wd->wor_value = value; current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static void write_WCS(struct wdt_device *wd, u32 value) { u64 current_time; u64 new_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); iowrite32(value, wd->base + WCS_REG_OFFSET); wd->wcs_value = value; current_time = ktime_get_ns(); new_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static void write_WCV(struct wdt_device *wd, u64 new_compare_value) { u64 current_time; u64 final_compare_value; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); current_time = ktime_get_ns(); if (new_compare_value > current_time) { final_compare_value = new_compare_value; } else { final_compare_value = current_time + wd->wdog_offset_value; } iowrite32((u32)(final_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(final_compare_value >> 32), wd->base + WCV_H_OFFSET); wd->last_feed_time = current_time; spin_unlock_irqrestore(&wd->lock, flags); } static irqreturn_t wdt_interrupt(int irq, void *dev_id) { struct wdt_device *wd = dev_id; u32 status; unsigned long flags; spin_lock_irqsave(&wd->lock, flags); // 读取状态寄存器 status = ioread32(wd->base + WCS_REG_OFFSET); // 检查状态位 if (!(status & WDT_WS0)) { status |= WDT_WS0; // 设置WS0位 iowrite32(status, wd->base + WCS_REG_OFFSET); } else if ((status & WDT_WS0) && !(status & WDT_WS1)) { status |= WDT_WS1; // 设置WS1位 iowrite32(status, wd->base + WCS_REG_OFFSET); spin_unlock_irqrestore(&wd->lock, flags); emergency_restart(); // 触发系统重启 return IRQ_HANDLED; } spin_unlock_irqrestore(&wd->lock, flags); return IRQ_HANDLED; } // 实现文件操作函数 static int wdt_open(struct inode *inode, struct file *file) { struct wdt_device *wd = container_of(file->private_data, struct wdt_device, wdd); if (test_and_set_bit(0, &wdt_is_open)) return -EBUSY; file->private_data = wd; return nonseekable_open(inode, file); } static ssize_t wdt_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { return 0; } static ssize_t wdt_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wdt_device *wd = file->private_data; if (len) { if (!nowayout) { size_t i; expect_close = 0; for (i = 0; i != len; i++) { char c; if (get_user(c, buf + i)) return -EFAULT; if (c == 'V') expect_close = 42; } } wdog_feed(&wd->wdd); } return len; } static int wdt_release(struct inode *inode, struct file *file) { struct wdt_device *wd = file->private_data; if (expect_close == 42) { wdog_stop(&wd->wdd); } else { pr_crit("Unexpected close, not stopping watchdog!\n"); wdog_feed(&wd->wdd); } clear_bit(0, &wdt_is_open); expect_close = 0; return 0; } static long wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd); int ret = 0; int time_val; void __user *argp = (void __user *)arg; int __user *p = argp; switch (cmd) { case WDIOC_KEEPALIVE: wdog_feed(wdd); break; case WDIOC_SETTIMEOUT: if (get_user(time_val, p)) return -EFAULT; heartbeat = time_val; wdd->timeout = heartbeat; wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC; wdog_feed(wdd); // 更新超时设置后立即喂狗 break; case WDIOC_GETTIMEOUT: return put_user(wdd->timeout, p); case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: if (get_user(time_val, p)) return -EFAULT; if (time_val & WDIOS_DISABLECARD) ret = wdog_stop(wdd); if (time_val & WDIOS_ENABLECARD) ret = wdog_start(wdd); break; default: ret = -ENOTTY; break; } return ret; } static const struct watchdog_info wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .identity = "Watchdog Timer", }; static const struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdog_start, .stop = wdog_stop, .ping = wdog_feed, .ioctl = wdt_ioctl, }; static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = wdt_open, .read = wdt_read, .write = wdt_write, .release = wdt_release, .unlocked_ioctl = wdt_ioctl, }; static int wdt_probe(struct platform_device *pdev) { struct wdt_device *wd; struct resource *res; int ret; u64 current_time; u64 initial_compare_value; u32 wcs_readback; wd = devm_kzalloc(&pdev->dev, sizeof(*wd), GFP_KERNEL); if (!wd) return -ENOMEM; // 获取内存资源 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "No memory resource\n"); return -ENODEV; } // 映射寄存器区域 wd->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(wd->base)) { dev_err(&pdev->dev, "Failed to map registers\n"); return PTR_ERR(wd->base); } // 初始化自旋锁 spin_lock_init(&wd->lock); // 获取中断 wd->irq = platform_get_irq(pdev, 0); if (wd->irq < 0) { dev_err(&pdev->dev, "No IRQ resource\n"); return wd->irq; } // 注册中断处理函数 ret = devm_request_irq(&pdev->dev, wd->irq, wdt_interrupt, IRQF_SHARED, "watchdog", wd); if (ret) { dev_err(&pdev->dev, "Failed to request IRQ\n"); return ret; } // 初始化看门狗寄存器 wd->wcs_value = ioread32(wd->base + WCS_REG_OFFSET); wd->wcs_value &= ~(1 << 0); // 禁用看门狗 iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET); // 设置超时值 (纳秒) wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC; wd->wor_value = wd->wdog_offset_value; iowrite32(wd->wor_value, wd->base + WOR_OFFSET); // 设置比较值 current_time = ktime_get_ns(); initial_compare_value = current_time + wd->wdog_offset_value; iowrite32((u32)(initial_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET); iowrite32((u32)(initial_compare_value >> 32), wd->base + WCV_H_OFFSET); // 启用看门狗 wd->wcs_value |= (1 << 0); iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET); wcs_readback = ioread32(wd->base + WCS_REG_OFFSET); wd->wcs_value = wcs_readback; // 初始化看门狗设备结构 wd->wdd.ops = &wdt_ops; wd->wdd.info = &wdt_info; wd->wdd.timeout = heartbeat; wd->wdd.min_timeout = 1; wd->wdd.max_timeout = 3600; wd->dev = &pdev->dev; wd->wdd.parent = &pdev->dev; wd->nowayout = nowayout; // 注册看门狗设备 ret = devm_watchdog_register_device(&pdev->dev, &wd->wdd); if (ret) { dev_err(&pdev->dev, "Failed to register watchdog device (err=%d)\n", ret); return ret; } // 设置驱动数据 platform_set_drvdata(pdev, wd); dev_info(&pdev->dev, "WDT driver initialized (heartbeat=%d sec, nowayout=%d)\n", heartbeat, nowayout); return 0; } static int wdt_remove(struct platform_device *pdev) { struct wdt_device *wd = platform_get_drvdata(pdev); // 停止看门狗 wdog_stop(&wd->wdd); dev_info(&pdev->dev, "Watchdog driver removed\n"); return 0; } #ifdef CONFIG_PM_SLEEP static int wdt_suspend(struct device *dev) { struct wdt_device *wd = dev_get_drvdata(dev); // 禁用看门狗 wdog_stop(&wd->wdd); return 0; } static int wdt_resume(struct device *dev) { struct wdt_device *wd = dev_get_drvdata(dev); // 恢复看门狗 wdog_start(&wd->wdd); wdog_feed(&wd->wdd); return 0; } static const struct dev_pm_ops wdt_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(wdt_suspend, wdt_resume) }; #endif static const struct of_device_id wdt_match[] = { { .compatible = "a,watchdog" }, {}, }; MODULE_DEVICE_TABLE(of, wdt_match); static struct platform_driver wdt_driver = { .probe = wdt_probe, .remove = wdt解释一下这个代码,每一步都在干什么
最新发布
12-16
static void common_hook(const unsigned char *buf, int count) { mm_segment_t fs; unsigned int buff_remain = 0; static unsigned long last_time = 0; struct timex txc; struct rtc_time tm; char timestr[32] = {0}; int len = 0; /* Filtering garbled output. * maybe we don't need filter any content */ #if CONFIG_LOG_FILTERING if (!(0x00 <= *buf && *buf <= 0x7f)) return; #endif if (1) { spin_lock(&kmonitorp->spin_lock); if (count < BUFF_SIZE - kmonitorp->cursor) { memcpy(&(kmonitorp->tmpbuf[kmonitorp->cursor]), buf, count); kmonitorp->cursor += count; } else { /* Cyclic writing */ buff_remain = BUFF_SIZE - kmonitorp->cursor; memcpy(&(kmonitorp->tmpbuf[kmonitorp->cursor]), buf, buff_remain); memcpy(&(kmonitorp->tmpbuf[0]), buf + buff_remain, count - buff_remain); /* set new cursor */ kmonitorp->cursor = count - buff_remain; } spin_unlock(&kmonitorp->spin_lock); } else { if (IS_ERR(kmonitorp->fp)) { return; } do_gettimeofday(&(txc.time)); if(txc.time.tv_sec > last_time) { rtc_time_to_tm(txc.time.tv_sec,&tm); len = sprintf(timestr, "[%d-%02d-%02d %02d:%02d:%02d]", tm.tm_year+1900,tm.tm_mon+1, tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec); } last_time = txc.time.tv_sec; mutex_lock(&kmonitorp->mutex); fs = get_fs(); set_fs(KERNEL_DS); if (kmonitorp->cursor) { memcpy(tmp_buf, kmonitorp->tmpbuf, kmonitorp->cursor); vfs_write(kmonitorp->fp, kmonitorp->tmpbuf, kmonitorp->cursor, &(kmonitorp->file_pos)); kmonitorp->cursor = 0; } vfs_write(kmonitorp->fp, buf, count, &(kmonitorp->file_pos)); file_check(); set_fs(fs); mutex_unlock(&kmonitorp->mutex); } #ifdef CONFIG_WRITE_FLASH_OOPS if (unlikely(oops)) { if (strstr(buf, endstr1) || strstr(buf, endstr2)) { UD_DEBUG("Identify the end of dump stack"); tmp_count = count; memcpy(tmp_buf, buf, count); kmonitor_panic_handler(NULL, NULL, NULL); oops = 0; } } #endif }
09-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

do2jiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值