]> Zhao Yanbai Git Server - kernel.git/commitdiff
修复__wait_event的bug;硬盘中断通知disk进程改用完成量
authoracevest <zhaoyanbai@126.com>
Tue, 23 Jul 2024 11:57:59 +0000 (19:57 +0800)
committeracevest <zhaoyanbai@126.com>
Tue, 23 Jul 2024 11:57:59 +0000 (19:57 +0800)
drivers/ide.c
drivers/ide.h
fs/buffer.c
include/completion.h
include/wait.h
kernel/completion.c
kernel/irq.c
kernel/task_disk.c
kernel/wait.c

index 091ae31f6eec7fe29db62b0f0b1c0b6050f14417..27616318280a3ae68e6fe31ee5c6659303a85496 100644 (file)
@@ -53,7 +53,8 @@ void ide_pci_init(pci_device_t *pci) {
         ide_pci_controller[i].request_queue.count = 0;
         INIT_LIST_HEAD(&ide_pci_controller[i].request_queue.list);
         semaphore_init(&ide_pci_controller[i].request_queue.sem, 0);
-        semaphore_init(&ide_pci_controller[i].disk_intr_sem, 0);
+        init_completion(&ide_pci_controller[i].intr_complete);
+        ide_pci_controller[i].intr_complete.name = i == 0 ? "ide0_intr_complete" : "ide1_intr_complete";
 
         atomic_set(&ide_pci_controller[i].request_cnt, 0);
         atomic_set(&ide_pci_controller[i].irq_cnt, 0);
@@ -142,8 +143,8 @@ void ide_irq_bh_handler(void *arg) {
     printlxy(MPL_IDE0 + channel, MPO_IDE, "IDE%d req %u irq %u consumed %u", channel,
              atomic_read(&(ide_ctrl->request_cnt)), ide_ctrl->irq_cnt, ide_ctrl->consumed_cnt);
 
-    // up里不会立即重新调度进程
-    up(&ide_ctrl->disk_intr_sem);
+    // complete会唤醒进程,但不会立即重新调度进程
+    complete(&ide_ctrl->intr_complete);
 }
 
 void ide_irq_handler(unsigned int irq, pt_regs_t *regs, void *devid) {
index 91a7c5d0714c2ec771f3cde0443d99363b2b6854..ea90706e8823b54458753b92a3f60624a30c102c 100644 (file)
@@ -184,9 +184,8 @@ typedef struct _ide_pci_controller {
     // 请求队列
     disk_request_queue_t request_queue;
 
-    // task disk与中断函数之间的信号量
-    // 初始化成0
-    semaphore_t disk_intr_sem;
+    // 中断处理函数通知task disk任务完成
+    completion_t intr_complete;
 
     atomic_t request_cnt;
     atomic_t irq_cnt;
@@ -212,6 +211,3 @@ typedef struct _ide_drive {
 extern ide_drive_t ide_drives[MAX_IDE_DRIVE_CNT];
 
 ide_drive_t *ide_get_drive(dev_t dev);
-
-void sleep_on_ide();
-void wait_on_ide();
index a754b5a2eaae99cbba5259e53724713e5864b822..823d38f501f0ed2da7030a6ff635b7aa9abad5f6 100644 (file)
@@ -124,7 +124,9 @@ again:
         irq_restore(iflags);
         retry++;
         // wait on free list
-        wait_on(&s->waitq);
+        // TODO
+        assert(0);
+        // wait_on(&s->waitq);
         goto again;
     }
 
@@ -156,7 +158,9 @@ void brelse(bbuffer_t *b) {
     assert(s != NULL);
     assert(s - store < 3);
 
-    wake_up(&s->waitq);
+    // TODO
+    assert(0);
+    // wake_up(&s->waitq);
 }
 
 bbuffer_t *bread(dev_t dev, uint64_t block, uint32_t size) {
index 93afe91a6133401f3e9eac1ba850b7f1b1ff8e0b..a367a86ed342053eb170de5e54e87207111ad944 100644 (file)
 #include <wait.h>
 
 typedef struct completion {
-    int done;
+    volatile int done;
     wait_queue_head_t wait;
+
+    // 仅用于调试
+    char *name;
 } completion_t;
 
 #define COMPLETION_INITIALIZER(x) \
     { 0, WAIT_QUEUE_HEAD_INITIALIZER(x).wait }
 
-void wait_completion(completion_t *x);
 void init_completion(completion_t *x);
 
+void wait_completion(completion_t *x);
+
+// 一次只唤醒一个进程
 void complete(completion_t *x);
index f94a0a731cb768c86ed217775623917c05847b08..2f726d4e2d920a9a148c589dc07027fb0069f4f5 100644 (file)
@@ -46,17 +46,46 @@ void __end_wait(wait_queue_entry_t *wq);
 
 // 使用这个函数的时候需要注意
 // 设置condition为真的语句和wake_up原子地执行
+// wake_up只唤醒不立即重新调度
 void wake_up(wait_queue_head_t *head);
+
+// nr == 0 代表唤醒所有
 void __wake_up(wait_queue_head_t *head, int nr);
 
 // 以下wait_event被定义成宏,而不是函数是因为condition
 // condition可能是一个表达式,如果定义成函数,往下传递就直接变成了一个值
 // 如果在某一步这个值变了,并不会反应在这些逻辑判断上
+#define __wait_event(head, condition)                  \
+    do {                                               \
+        DECLARE_WAIT_QUEUE_ENTRY(__wait, current);     \
+        unsigned long flags;                           \
+        while (1) {                                    \
+            irq_save(flags);                           \
+            prepare_to_wait(head, &__wait, TASK_WAIT); \
+            if ((condition)) {                         \
+                __end_wait(&__wait);                   \
+                irq_restore(flags);                    \
+                break;                                 \
+            } else {                                   \
+                irq_restore(flags);                    \
+                void schedule();                       \
+                schedule();                            \
+                __end_wait(&__wait);                   \
+            }                                          \
+        }                                              \
+    } while (0)
 
-// 只要保证condition设置为真时,它是和wake_up一起原子执行的
-// 那就能保证condition为真是如下__wait_event和wait_event里的if不出问题
-// 也就是不会出现if-break后进程又再次因为其它原因阻塞后,又被wake_up的逻辑
-// 设置为READY状态
+#if 0
+// __wait_event这个逻辑参考自Linux内核,但是发现它有BUG
+// 第一个问题
+// 1. 在进入到__wait_event后,prepare_to_wait前其它进程通过wake_up唤醒当前进程
+// 2. prepare_to_wait把当前进程设置为TASK_WAIT
+// 3. prepare_to_wait最后一句irq_restore(flags);执行完后 其实 condition 已经为true了
+// 4. 从if((conditon))到break再到__end_wait前都有可能被中断打断并重新schedule()
+//    只要在__end_wait的irq_save(flags);完全关闭中断前被中断打断并schedule把当前进程换下CPU,那么这次唤醒就会丢失
+//    如果只有一次唤醒,那么这个唤醒丢失后,当前进程就会一直处于WAIT状态并且永远不会再被调度到
+//
+// 但按理Linux内核不应该会出现这种BUG,还没研究明白,先把这段代码放在这里
 #define __wait_event(head, condition)                  \
     do {                                               \
         DECLARE_WAIT_QUEUE_ENTRY(__wait, current);     \
@@ -70,13 +99,12 @@ void __wake_up(wait_queue_head_t *head, int nr);
         }                                              \
         __end_wait(&__wait);                           \
     } while (0)
-
-#define wait_event(head, condition)          \
-    do {                                     \
-        if (!(condition)) {                  \
-            __wait_event(head, (condition)); \
-        }                                    \
+#endif
+
+#define wait_event(head, condition)      \
+    do {                                 \
+        if ((condition)) {               \
+            break;                       \
+        }                                \
+        __wait_event(head, (condition)); \
     } while (0)
-
-// 无条件wait
-void wait_on(wait_queue_head_t *head);
index 8d2ab8b6bc68e5dc905ada2d93cf1b17fe8a602c..ea7d7b4dc7cc73c96de97f5b85b12afffc15e01d 100644 (file)
 #include <sched.h>
 
 void wait_completion(completion_t *x) {
-    //
-    wait_event(&x->wait, (x->done == 1));
+    wait_event(&x->wait, (x->done != 0));
+    x->done--;
 }
 
 void complete(completion_t *x) {
     uint32_t iflags;
     irq_save(iflags);
-    x->done = 1;
-    // wake_up(&x->wait);
-    __wake_up(&x->wait, 0);
+    x->done++;
+    __wake_up(&x->wait, 1);
     irq_restore(iflags);
 }
 
 void init_completion(completion_t *x) {
     x->done = 0;
     init_wait_queue_head(&x->wait);
+
+    // 测试字段
+    x->name = 0;
 }
index d688d15fc049bde1cb2e4436f7f9908b95af2385..36f1b615affb05357ec65b42cf3d0fc53314aec7 100644 (file)
@@ -142,6 +142,7 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t *regs) {
     {
         enable_irq();
 
+        // 这里面不能执行任务切换操作,比如信号量相关操作
         irq_bh_handler();
 
         disable_irq();
index 43697e5bf38a6fbde750bbd4c81bfa75eb5737d2..40a2427409ce6ce584720b1f21212f3591f91cf9 100644 (file)
@@ -94,6 +94,8 @@ void disk_task_entry(void *arg) {
         uint64_t pos = r->pos + drv->partions[part_id].lba_start;
         assert(pos < drv->partions[part_id].lba_end);
 
+        // init_completion(&ide_ctrl->intr_complete);
+
         switch (r->command) {
         case DISK_REQ_IDENTIFY:
             printk("try to read disk drive %u identify", drv_no);
@@ -116,8 +118,7 @@ void disk_task_entry(void *arg) {
         }
 
         // 等待硬盘中断
-        // printk("down ide req\n");
-        down(&ide_ctrl->disk_intr_sem);
+        wait_completion(&ide_ctrl->intr_complete);
 
         // 读数据
         if (DISK_REQ_IDENTIFY == r->command) {
index bb4fd0fdfae1a14d2d2abd2eb51e70a65363353d..601feac5ffc1199b4d21c3ab09a297143e4ea10d 100644 (file)
@@ -17,15 +17,21 @@ volatile void init_wait_queue_head(wait_queue_head_t *wqh) { INIT_LIST_HEAD(&(wq
 volatile void prepare_to_wait(wait_queue_head_t *head, wait_queue_entry_t *wqe, unsigned int state) {
     unsigned long flags;
     irq_save(flags);
+    // 进程可能等待一个condition满足后被唤醒
+    // 然后又发现这个conditon又不满足了,需要继续wait
+    // 这时候又会把自己加到等待队列里
+    // 所以这里需要加一个判断,如果已经加过了,就不需要再加了,不然会出问题
     if (list_empty(&wqe->entry)) {
         list_add_tail(&wqe->entry, &head->task_list);
     }
     set_current_state(state);
+    current->reason = "p_wait";
     irq_restore(flags);
 }
 
 volatile void __end_wait(wait_queue_entry_t *wqe) {
     set_current_state(TASK_READY);
+    current->reason = "e_wait";
     unsigned long flags;
     irq_save(flags);
     list_del_init(&wqe->entry);
@@ -72,21 +78,3 @@ volatile void wake_up(wait_queue_head_t *head) {
     //
     __wake_up(head, 0);  // wake up all
 }
-
-volatile void wait_on(wait_queue_head_t *head) {
-    DECLARE_WAIT_QUEUE_ENTRY(wait, current);
-
-    unsigned long flags;
-    irq_save(flags);
-
-    current->state = TASK_WAIT;
-    current->reason = "sleep_on";
-
-    list_add_tail(&wait.entry, &head->task_list);
-
-    irq_restore(flags);
-
-    schedule();
-
-    __end_wait(&wait);
-}