From: acevest Date: Tue, 23 Jul 2024 11:57:59 +0000 (+0800) Subject: 修复__wait_event的bug;硬盘中断通知disk进程改用完成量 X-Git-Url: http://zhaoyanbai.com/repos/?a=commitdiff_plain;h=bf778f66604a78636b26cf70bea414aea1003aa9;p=kernel.git 修复__wait_event的bug;硬盘中断通知disk进程改用完成量 --- diff --git a/drivers/ide.c b/drivers/ide.c index 091ae31..2761631 100644 --- a/drivers/ide.c +++ b/drivers/ide.c @@ -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) { diff --git a/drivers/ide.h b/drivers/ide.h index 91a7c5d..ea90706 100644 --- a/drivers/ide.h +++ b/drivers/ide.h @@ -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(); diff --git a/fs/buffer.c b/fs/buffer.c index a754b5a..823d38f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -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) { diff --git a/include/completion.h b/include/completion.h index 93afe91..a367a86 100644 --- a/include/completion.h +++ b/include/completion.h @@ -12,14 +12,19 @@ #include 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); diff --git a/include/wait.h b/include/wait.h index f94a0a7..2f726d4 100644 --- a/include/wait.h +++ b/include/wait.h @@ -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); diff --git a/kernel/completion.c b/kernel/completion.c index 8d2ab8b..ea7d7b4 100644 --- a/kernel/completion.c +++ b/kernel/completion.c @@ -11,20 +11,22 @@ #include 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; } diff --git a/kernel/irq.c b/kernel/irq.c index d688d15..36f1b61 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -142,6 +142,7 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t *regs) { { enable_irq(); + // 这里面不能执行任务切换操作,比如信号量相关操作 irq_bh_handler(); disable_irq(); diff --git a/kernel/task_disk.c b/kernel/task_disk.c index 43697e5..40a2427 100644 --- a/kernel/task_disk.c +++ b/kernel/task_disk.c @@ -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) { diff --git a/kernel/wait.c b/kernel/wait.c index bb4fd0f..601feac 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -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); -}