当前位置: 首页 > news >正文

网站开发发现趋势国家电网账号注册网站帐号是什么

网站开发发现趋势,国家电网账号注册网站帐号是什么,做seo网站的公司哪家好,新网站网页收录在上一篇《一次降低进程IO延迟的性能优化实践——基于block层bfq调度器》基础上#xff0c;本文主要总结实现该IO性能优化过程遇到的 IO卡死、IO重复派发、内核crash等问题。 1#xff1a;IO重复派发触发了crash 在初版代码编写完成后#xff0c;启动fio测试cat读取文件本文主要总结实现该IO性能优化过程遇到的 IO卡死、IO重复派发、内核crash等问题。 1IO重复派发触发了crash 在初版代码编写完成后启动fio测试cat读取文件有很大概率触发了内核crash现场如下 PID: 11602  TASK: ffff95f3092ddf00  CPU: 3   COMMAND: cat #0 [ffffa67081ceb390] machine_kexec at ffffffff8525bf3e #1 [ffffa67081ceb3e8] __crash_kexec at ffffffff8536072d #2 [ffffa67081ceb4b0] panic at ffffffff852b5dc7 #3 [ffffa67081ceb530] __warn.cold.12 at ffffffff852b5fee #4 [ffffa67081ceb538] blk_mq_start_request at ffffffff856075d0 #5 [ffffa67081ceb560] blk_mq_start_request at ffffffff856075d0 #6 [ffffa67081ceb590] do_error_trap at ffffffff8521f9de #7 [ffffa67081ceb5d0] do_invalid_op at ffffffff8521fe36 #8 [ffffa67081ceb5f0] invalid_op at ffffffff85c00d84    [exception RIP: blk_mq_start_request496]    RIP: ffffffff856075d0  RSP: ffffa67081ceb6a0  RFLAGS: 00010202    RAX: 0000000000000001  RBX: ffff95f28fc57810  RCX: 0000000000000018    RDX: 00000000004b1dc2  RSI: ffff95f28fc57810  RDI: ffff95f297722758    RBP: ffff95f38f868000   R8: ffffa67081ceb7e8   R9: 0000000000000000    R10: 0000000000000000  R11: 0000000000000011  R12: ffff95f296143000    R13: ffff95f2987fe000  R14: ffff95f2987fe050  R15: ffffa67081ceb788    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018 #9 [ffffa67081ceb6b8] scsi_queue_rq at ffffffff857d1a51#10 [ffffa67081ceb708] blk_mq_dispatch_rq_list at ffffffff85609f4c#11 [ffffa67081ceb7d8] blk_mq_do_dispatch_sched at ffffffff8560f4ba#12 [ffffa67081ceb830] __blk_mq_sched_dispatch_requests at ffffffff8560ff99#13 [ffffa67081ceb890] blk_mq_sched_dispatch_requests at ffffffff85610020#14 [ffffa67081ceb8a0] __blk_mq_run_hw_queue at ffffffff856076a1#15 [ffffa67081ceb8b8] __blk_mq_delay_run_hw_queue at ffffffff85607f61#16 [ffffa67081ceb8e0] blk_mq_sched_insert_requests at ffffffff85610351#17 [ffffa67081ceb918] blk_mq_flush_plug_list at ffffffff8560b4d6#18 [ffffa67081ceb998] blk_flush_plug_list at ffffffff855ffbe7#19 [ffffa67081ceb9e8] blk_mq_make_request at ffffffff8560ad38#20 [ffffa67081ceba78] generic_make_request at ffffffff855fe85f#21 [ffffa67081cebad0] submit_bio at ffffffff855feadc#22 [ffffa67081cebb10] ext4_mpage_readpages at ffffffffc08eead1 [ext4]#23 [ffffa67081cebbf8] read_pages at ffffffff8543743b#24 [ffffa67081cebc70] __do_page_cache_readahead at ffffffff85437721…………………. 触发crash的源码位置如下 void blk_mq_start_request(struct request *rq)                                                                                                                                                                   {    struct request_queue *q rq-q;    blk_mq_sched_started_request(rq);    trace_block_rq_issue(q, rq);    if (test_bit(QUEUE_FLAG_STATS, q-queue_flags)) {            rq-io_start_time_ns ktime_get_ns();            rq_aux(rq)-stats_sectors blk_rq_sectors(rq);            rq-rq_flags | RQF_STATS;            rq_qos_issue(q, rq);    }    WARN_ON_ONCE(blk_mq_rq_state(rq) ! MQ_RQ_IDLE);//这里crashblk_add_timer(rq);//标记rq-state 为MQ_RQ_IN_FLIGHT表示IO请求派发给磁盘驱动了   WRITE_ONCE(rq-state, MQ_RQ_IN_FLIGHT);}static inline enum mq_rq_state blk_mq_rq_state(struct request *rq){    return READ_ONCE(rq-state);} crash过程是在把rq派发给磁盘驱动过程执行blk_mq_start_request()函数中rq-state不是MQ_RQ_IDLE然后就主动触发WARN_ON_ONCE而crash。按照经验crash现场的RDI寄存器就是blk_mq_start_request()函数传输rq指针看下这个rq的参数 crash request ffff95f297722758   __data_len 0, //date_len 有问题  tag -275282040, //tag 有问题  __sector 18446638524612970376, //扇区地址明显有问题  bio 0x0, //这个bio有问题  biotail 0x0, rq_disk 0x0, /rq_disk 不可能是NULL  state MQ_RQ_IDLE, 到这里怀疑rdi:0xffff95f297722758应该不是blk_mq_start_request()函数传参rq指针因为打印的rq结构体变量根本不符合常理对于不符合常理的就要另找他法。 因为这个case比较容易复现大概率跟我在_bfq_dispatch_request()添加的代码有关。于是在blk_mq_start_request()和__bfq_dispatch_request()中添加一下调试信息如下红色代码 void blk_mq_start_request(struct request *rq)                                                                       {        struct request_queue *q rq-q;        blk_mq_sched_started_request(rq);        trace_block_rq_issue(q, rq);        if (test_bit(QUEUE_FLAG_STATS, q-queue_flags)) {                rq-io_start_time_ns ktime_get_ns();                rq_aux(rq)-stats_sectors blk_rq_sectors(rq);                rq-rq_flags | RQF_STATS;                rq_qos_issue(q, rq);        }        printk(%s %s %d rq:0x%llx rq-rq_disk:0x%llx \n,__func__,current-comm,current-pid,(u64)rq,(u64)rq-rq_disk);        WARN_ON_ONCE(blk_mq_rq_state(rq) ! MQ_RQ_IDLE);        blk_add_timer(rq);        //标记rq-state 为MQ_RQ_IN_FLIGHT表示IO请求派发给磁盘驱动了        WRITE_ONCE(rq-state, MQ_RQ_IN_FLIGHT);}static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx){    ..................    if(bfqd-bfq_high_io_prio_mode)    {       //在 bfq_high_io_prio_mode 非0时间的5s内如果遇到非high prio io并且驱动队列IO个数大于限制则把不派发该IO而是临时添加到bfq_high_prio_tmp_list链表       if((bfqd-rq_in_driver 16) (bfqd-bfq_high_prio_tmp_list_rq_count 100)){        //把rq从原有链表删掉并把rq移动到bfq_high_prio_tmp_list链表尾派发时是从bfq_high_prio_tmp_list链表头取出rq保证先到先派发        list_add_tail(rq-queuelist,bfqd-bfq_high_prio_tmp_list);        bfqd-bfq_high_prio_tmp_list_rq_count ;        p_process_io_info_tmp-block_io_count ;        printk(%s %s %d rq:0x%llx bfqq:0x%llx pid:%d bfqq-dispatched:%d bfq_high_prio_tmp_list_rq_count:%d rq_in_driver:%d !!!!!!!!!!!!\n,__func__,current-comm,current-pid,(u64)rq,(u64)bfqq,bfqq-pid,bfqq-dispatched,bfqd-bfq_high_prio_tmp_list_rq_count,bfqd-rq_in_driver);        goto exit1;       }    }    ..................} 等下次触发crash内核打印 blk_mq_start_request cat 15092 rq:0xffff8eff2401d990 rq-rq_disk:0xffff8efe1b1b4000看下它的成员信息 crash request 0xffff8eff2401d990struct request {  __data_len 1048576,   tag 86,   __sector 3468288,   bio 0xffff8efd875e8300,   biotail 0xffff8efd875e8300,   rq_disk 0xffff8efe1b1b4000,  state MQ_RQ_IN_FLIGHT, 看来这次的rq指针是正确的刚才通过rdi获取blk_mq_start_request()函数传参是有问题的。这个rq-state是MQ_RQ_IN_FLIGHT就是说该rq已经派发给磁盘驱动了在传输完成前又派发给磁盘驱动显然重复了。再看下crash前的内核打印印证了我的想法 //rq:0xffff8eff2401d990 这里被插入 bfq_high_prio_tmp_list_rq_count 链表[  132.559190] __bfq_dispatch_request cat 15092 rq:0xffff8eff2401d990 bfqq:0xffff8efe1ba0b200 pid:15092 bfqq-dispatched:17 bfq_high_prio_tmp_list_rq_count:1 rq_in_driver:16 !!!!!!!!!!!!1//rq:0xffff8eff2401d990  被派发[  132.559244] blk_mq_start_request cat 15092 rq:0xffff8eff2401d990 rq-rq_disk:0xffff8efe1b1b4000//rq:0xffff8eff2401d990 又被派发[  132.561350] blk_mq_start_request cat 15092 rq:0xffff8eff2401d990 rq-rq_disk:0xffff8efe1b1b4000[  132.561398] WARNING: CPU: 1 PID: 15092 at block/blk-mq.c:696 blk_mq_start_request0x128/0x263[  132.561401] Kernel panic - not syncing: panic_on_warn set ...[  132.561409] CPU: 1 PID: 15092 Comm: cat Kdump: loaded Tainted: G            E    ---------r-  - 4.18.0 #2[  132.561412] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 02/27/2020[  132.561414] Call Trace:[  132.561431]  dump_stack0x5c/0x80[  132.561437]  panic0xe7/0x2a9[  132.561443]  ? blk_mq_start_request0x128/0x263[  132.561447]  __warn.cold.120x31/0x33[  132.561450]  ? blk_mq_start_request0x128/0x263[  132.561454]  ? blk_mq_start_request0x128/0x263[  132.561457]  report_bug0xb1/0xd0- 显然rq:0xffff8eff2401d990就是被连续派发了两次就得看看我添加的代码哪里有问题了 static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx){    ...............    rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq);    if (rq) {        if(bfqd-queue-high_io_prio_enable)        {            if(rq-rq_flags RQF_HIGH_PRIO){//高优先级IO                //第一次遇到high prio io置1 bfq_high_io_prio_mode启动5s定时器定时到了对bfq_high_io_prio_mode清0                if(bfqd-bfq_high_io_prio_mode 0){                    bfqd-bfq_high_io_prio_mode 1;                    hrtimer_start(bfqd-bfq_high_prio_timer, ms_to_ktime(5000),HRTIMER_MODE_REL);                }                p_process_io_info_tmp-high_prio_io_count ;                p_process_io_info_tmp-dispatch_io_count;            }            else非高优先级IO            {               p_process_io_info_tmp-high_not_prio_io_count ;               if(bfqd-bfq_high_io_prio_mode)               {                   //在 bfq_high_io_prio_mode 非0时间的5s内如果遇到非high prio io并且驱动队列IO个数大于限制则把不派发该IO而是临时添加到bfq_high_prio_tmp_list链表                   if((bfqd-rq_in_driver 16) (bfqd-bfq_high_prio_tmp_list_rq_count 100)){                        //把rq从原有链表删掉并把rq移动到bfq_high_prio_tmp_list链表尾派发时是从bfq_high_prio_tmp_list链表头取出rq保证先到先派发                       list_add_tail(rq-queuelist,bfqd-bfq_high_prio_tmp_list);                       bfqd-bfq_high_prio_tmp_list_rq_count ;                       p_process_io_info_tmp-block_io_count ;                    ///bug就出在这里这里的rq添加到 bfq_high_prio_tmp_list 链表后本次就不应该再派发了!!!!!!!!!但是却 goto exit1 该函数return rq返回该rq并派发了!!!!!!!!!!!!正确的做法是rq NULL赋值rq为NULL                      goto exit1;                   }               }            }        }            /*如果 bfq_high_prio_tmp_list 链表上有rq要派发不执行这里的rq_in_driver在下边的exit那里会执行当echo 0 /sys/block/sdb/process_high_io_prio 置1再置0后这个if判断就起作用了。没这个判断这里会bfqd-rq_in_driver下边的if里再bfqd-rq_in_driver导致rq_in_driver泄漏*/        if((rq-rq_flags RQF_HIGH_PRIO) || list_empty(bfqd-bfq_high_prio_tmp_list)){inc_in_driver_start_rq:            bfqd-rq_in_driver;start_rq:            rq-rq_flags | RQF_STARTED;        }    }exit:    //1:如果是高优先级IO该if不成立直接跳过。 2:如果非高优先级IO则把rq添加到bfq_high_prio_tmp_list尾从链表头选一个rq派发 3:如果rq是NULL则也从bfq_high_prio_tmp_list选一个rq派发        if(!direct_dispatch ((rq !(rq-rq_flags RQF_HIGH_PRIO)) || !rq)){           if(!list_empty(bfqd-bfq_high_prio_tmp_list)){                 if(rq){                     list_add_tail(rq-queuelist,bfqd-bfq_high_prio_tmp_list);                     bfqd-bfq_high_prio_tmp_list_rq_count ;                     if(p_process_io_info_tmp)                         p_process_io_info_tmp-block_io_count2;                 }                 rq list_first_entry(bfqd-bfq_high_prio_tmp_list, struct request, queuelist);                  list_del_init(rq-queuelist);                 bfqd-bfq_high_prio_tmp_list_rq_count --;                 bfqd-rq_in_driver;                 rq-rq_flags | RQF_STARTED;            }        }exit1:    return rq;} 问题就出在红色代码goto exit1哪里那里的rq添加到 bfq_high_prio_tmp_list 链表后本次就不应该再派发了但是却 goto exit1 该函数return rq返回该rq并派发了。后续再从bfq_high_prio_tmp_list 链表链表取出该rq就会导致rq重复派发了。解决方法很简单先rq NULL再goto exit1这样就避免第一次派发该rq了。 2派发IO时遇到卡死 2.1 因bfq_has_work()返回false导致一直卡死 上一个问题解决了新的问题又来了。启动fio压测竟然卡死了kill -9 fio进程也不行。系统有很多D进程启动crash工具看下D进程信息 crash ps -m | grep UN[0 00:06:40.712] [UN]  PID: 2767   TASK: ffff8cb3ff450000  CPU: 0   COMMAND: fio[0 00:06:40.718] [UN]  PID: 2780   TASK: ffff8cb3c9d5c740  CPU: 3   COMMAND: fio[0 00:06:40.719] [UN]  PID: 2773   TASK: ffff8cb3c9d317c0  CPU: 2   COMMAND: fio[0 00:06:40.727] [UN]  PID: 2769   TASK: ffff8cb3c9d0df00  CPU: 3   COMMAND: fio[0 00:06:40.731] [UN]  PID: 2778   TASK: ffff8cb3c9d5df00  CPU: 3   COMMAND: fio[0 00:06:40.735] [UN]  PID: 2772   TASK: ffff8cb3c9d08000  CPU: 3   COMMAND: fio[0 00:06:40.738] [UN]  PID: 2775   TASK: ffff8cb3c9d32f80  CPU: 3   COMMAND: fio[0 00:06:40.742] [UN]  PID: 2770   TASK: ffff8cb3c9d0af80  CPU: 3   COMMAND: fio[0 00:06:40.744] [UN]  PID: 2768   TASK: ffff8cb3c9d097c0  CPU: 2   COMMAND: fio[0 00:06:40.757] [UN]  PID: 2777   TASK: ffff8cb3c9d30000  CPU: 2   COMMAND: fio[0 00:06:40.768] [UN]  PID: 2782   TASK: ffff8cb3c9d597c0  CPU: 3   COMMAND: fio[0 00:06:40.769] [UN]  PID: 2764   TASK: ffff8cb3ff454740  CPU: 0   COMMAND: fio 看下栈回溯 crash bt 2764PID: 2764   TASK: ffff8cb3ff454740  CPU: 0   COMMAND: fio #0 [ffffb2348279bb70] __schedule at ffffffffa84c8826 #1 [ffffb2348279bc08] schedule at ffffffffa84c8cb8 #2 [ffffb2348279bc18] rwsem_down_write_slowpath at ffffffffa7d105ed #3 [ffffb2348279bc90] bfq_has_work at ffffffffc08054d2 [bfq] #4 [ffffb2348279bca0] _cond_resched at ffffffffa84c8d95 #5 [ffffb2348279bcd8] ext4_file_write_iter at ffffffffc08c29bb [ext4] #6 [ffffb2348279bd38] aio_write at ffffffffa7f31206 #7 [ffffb2348279be40] io_submit_one at ffffffffa7f31581 #8 [ffffb2348279beb8] __x64_sys_io_submit at ffffffffa7f31b82 #9 [ffffb2348279bf38] do_syscall_64 at ffffffffa7c0419b#10 [ffffb2348279bf50] entry_SYSCALL_64_after_hwframe at ffffffffa86000adcrash bt 2780PID: 2780   TASK: ffff8cb3c9d5c740  CPU: 3   COMMAND: fio #0 [ffffb23482983b30] __schedule at ffffffffa84c8826 #1 [ffffb23482983bc8] schedule at ffffffffa84c8cb8 #2 [ffffb23482983bd8] rwsem_down_read_slowpath at ffffffffa84cbd05 #3 [ffffb23482983c88] ext4_direct_IO at ffffffffc08d6e5d [ext4] #4 [ffffb23482983cf0] generic_file_read_iter at ffffffffa7e2da7f #5 [ffffb23482983d38] aio_read at ffffffffa7f313a5 #6 [ffffb23482983e40] io_submit_one at ffffffffa7f3165b #7 [ffffb23482983eb8] __x64_sys_io_submit at ffffffffa7f31b82 #8 [ffffb23482983f38] do_syscall_64 at ffffffffa7c0419b #9 [ffffb23482983f50] entry_SYSCALL_64_after_hwframe at ffffffffa86000adcrash bt 2776PID: 2776   TASK: ffff8cb3c9d34740  CPU: 2   COMMAND: fio #0 [ffffb23482953958] __schedule at ffffffffa84c8826 #1 [ffffb234829539f0] schedule at ffffffffa84c8cb8 #2 [ffffb23482953a00] io_schedule at ffffffffa84c90d2 #3 [ffffb23482953a10] bit_wait_io at ffffffffa84c94dd #4 [ffffb23482953a20] __wait_on_bit_lock at ffffffffa84c934d #5 [ffffb23482953a58] out_of_line_wait_on_bit_lock at ffffffffa84c9421 #6 [ffffb23482953aa8] do_get_write_access at ffffffffc083ae68 [jbd2] #7 [ffffb23482953b08] jbd2_journal_get_write_access at ffffffffc083b10c [jbd2] #8 [ffffb23482953b28] __ext4_journal_get_write_access at ffffffffc08b63f6 [ext4] #9 [ffffb23482953b58] ext4_reserve_inode_write at ffffffffc08d35a6 [ext4]#10 [ffffb23482953b80] ext4_mark_inode_dirty at ffffffffc08d37d1 [ext4]#11 [ffffb23482953bf0] ext4_dirty_inode at ffffffffc08d8a15 [ext4]#12 [ffffb23482953c08] __mark_inode_dirty at ffffffffa7f0aa6a#13 [ffffb23482953c40] generic_update_time at ffffffffa7ef76e6#14 [ffffb23482953c50] file_update_time at ffffffffa7ef7b01#15 [ffffb23482953c98] __generic_file_write_iter at ffffffffa7e2dd38#16 [ffffb23482953cd8] ext4_file_write_iter at ffffffffc08c2761 [ext4]#17 [ffffb23482953d38] aio_write at ffffffffa7f31206#18 [ffffb23482953e40] io_submit_one at ffffffffa7f31581#19 [ffffb23482953eb8] __x64_sys_io_submit at ffffffffa7f31b82#20 [ffffb23482953f38] do_syscall_64 at ffffffffa7c0419b#21 [ffffb23482953f50] entry_SYSCALL_64_after_hwframe at ffffffffa86000ad 有几个fio进程的栈回溯竟然是bfq_has_work这里边没有调用什么锁呀很奇怪难道卡死根源跟bfq_has_work有关。看下它的源码 //返回0则blk_mq_do_dispatch_sched()中就无法派发继续派发IO了static bool bfq_has_work(struct blk_mq_hw_ctx *hctx){    struct bfq_data *bfqd hctx-queue-elevator-elevator_data;        //list_empty_careful(bfqd-dispatch)返回NULL说明该链表上有rq派发返回1    return !list_empty_careful(bfqd-dispatch) ||    //bfq_tot_busy_queues(bfqd)大于0说明还有active bfqq则派发该bfqq上的rq此时返回1        bfq_tot_busy_queues(bfqd) 0;} 一般是派发blk-mq派发blk_mq_do_dispatch_sched()函数中会调用bfq_has_work()函数源码如下 static int blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx){    struct request_queue *q hctx-queue;    struct elevator_queue *e q-elevator;    LIST_HEAD(rq_list);    int ret 0;    do {        struct request *rq;        //调用bfq_has_work        if (e-type-ops.has_work !e-type-ops.has_work(hctx))            break;        if (!list_empty_careful(hctx-dispatch)) {            ret -EAGAIN;            break;        }        if (!blk_mq_get_dispatch_budget(hctx))            break;        //调用bfq调度器函数 bfq_dispatch_request        rq e-type-ops.dispatch_request(hctx);        if (!rq) {            blk_mq_put_dispatch_budget(hctx);            blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY);            break;        }        list_add(rq-queuelist, rq_list);    /*取出rq_list链表上的req派发给磁盘驱动如果因驱动队列繁忙或者nvme硬件繁忙导致派发失败则把req添加hctx-dispatch等稍后派发遇到req派发失败返回false退出while循环*/    } while (blk_mq_dispatch_rq_list(q, rq_list, true));    return ret;} 当 bfq_has_work 返回0原本说明bfq没有IO可派发了blk_mq_do_dispatch_sched()就不再派发IO了。但是我对bfq派发IO的bfq_dispatch_request函数做了优化增加了一个 bfq_high_prio_tmp_list链表保存普通优先级的rq。当bfq空闲时bfq_tot_busy_queues(bfqd)返回0但是bfq_high_prio_tmp_list链表上还有rq要派发此时还需要继续派发rq。fio暂存在 bfq_high_prio_tmp_list链表上的rq得不到派发fio进程就卡主不能再派发新rq除非老的rq派发完成。简单说这种情况下要想判断bfq是否还有rq没派发必须判断bfq_high_prio_tmp_list链表上是否有IO。于是在bfq_has_work()函数中添加如下红色代码 static bool bfq_has_work(struct blk_mq_hw_ctx *hctx)                                                                                                                                       {        struct bfq_data *bfqd hctx-queue-elevator-elevator_data;        return !list_empty_careful(bfqd-dispatch) ||               !list_empty(bfqd-bfq_high_prio_tmp_list) ||                bfq_tot_busy_queues(bfqd) 0;} ok这个问题解决了但是新的问题又来了。 2.2  bfqq-dispatched泄漏导致的卡死 这个问题的表现也是派发IO的fio或者cat进程卡死同样也是有很多D进程ps -eLlf | grep fio |awk {print $6} | while read line;do echo *********;cat /proc/$line/stack;done  看下栈回溯主要是以下两类 *********[0] rwsem_down_write_slowpath0x32d/0x4e0[0] ext4_file_write_iter0x3cb/0x3e0 [ext4][0] aio_write0xf6/0x1c0[0] io_submit_one0x131/0x3c0[0] __x64_sys_io_submit0xa2/0x180[0] do_syscall_640x5b/0x1a0[0] entry_SYSCALL_64_after_hwframe0x65/0xca*********[0] blk_mq_get_tag0x119/0x270[0] __blk_mq_alloc_request0xb1/0x100[0] blk_mq_make_request0x14e/0x5d0[0] generic_make_request0xcf/0x310[0] submit_bio0x3c/0x160[0] do_blockdev_direct_IO0x21e6/0x2e60[0] ext4_direct_IO0x247/0x730 [ext4][0] generic_file_direct_write0x93/0x160[0] __generic_file_write_iter0xb7/0x1c0[0] ext4_file_write_iter0x171/0x3e0 [ext4][0] aio_write0xf6/0x1c0[0] io_submit_one0x131/0x3c0[0] __x64_sys_io_submit0xa2/0x180[0] do_syscall_640x5b/0x1a0[0] entry_SYSCALL_64_after_hwframe0x65/0xca 分析根源应该是有进程 __blk_mq_alloc_request-blk_mq_get_tag 分配tag失败导致的。 在派发IO的__bfq_dispatch_request()函数最后添加如下红色代码调试信息。 static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx){exit:    ..............    printk(5:%s %s %d  dispatch rq:0x%llx bfq_high_io_prio_count:%d rq_in_driver:%d\n,__func__,current-comm,current-pid,(u64)rq,bfqd-bfq_high_io_prio_count,bfqd-rq_in_driver);    return rq;} 卡死时刷屏打印如下信息 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0 这是blk-mq驱动了内核线程在疯狂的派发rq但是派发的rq一直是NULL。正常情况应该会退出派发的 看下 497 派发IO的函数流程为什么会一直派发rq呢执行这个命令stap --all-modules  -ve probe module(bfq).function(bfq_dispatch_request) {printf(%s %d\n,execname(),tid()) print_backtrace()}刷屏打印 kworker/3:1H 497 0xffffffffc06b3950 : bfq_dispatch_request0x0/0x9f0 [bfq] 0xffffffffa480f385 : blk_mq_do_dispatch_sched0xc5/0x160 [kernel] 0xffffffffa480feb9 : __blk_mq_sched_dispatch_requests0x189/0x1e0 [kernel] 0xffffffffa480ff40 : blk_mq_sched_dispatch_requests0x30/0x60 [kernel] 0xffffffffa48076a1 : __blk_mq_run_hw_queue0x51/0xd0 [kernel] 0xffffffffa44d3477 : process_one_work0x1a7/0x360 [kernel] 0xffffffffa44d3b40 : worker_thread0x30/0x390 [kernel] 0xffffffffa44d9502 : kthread0x112/0x130 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] (inexact) 为什么 kworker/3:1H 进程会刷屏执行 __blk_mq_run_hw_queue 而最终疯狂派发 rq 呢继续执行stap --all-modules  -ve probe kernel.function(blk_mq_do_dispatch_sched).return {if(tid() 497) {printf(%s %d\n,execname(),tid()) print_backtrace()}}调试刷屏打印 kworker/3:1H 497Returning from:  0xffffffffa480f2c0 : blk_mq_do_dispatch_sched0x0/0x160 [kernel]Returning to  :  0xffffffffa480feb9 : __blk_mq_sched_dispatch_requests0x189/0x1e0 [kernel] 0xffffffffa480ff40 : blk_mq_sched_dispatch_requests0x30/0x60 [kernel] 0xffffffffa48076a1 : __blk_mq_run_hw_queue0x51/0xd0 [kernel] 0xffffffffa44d3477 : process_one_work0x1a7/0x360 [kernel] 0xffffffffa44d3b40 : worker_thread0x30/0x390 [kernel] 0xffffffffa44d9502 : kthread0x112/0x130 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] (inexact) 源码分析这是blk-mq驱动启动的内核线程而启动的根源在blk_mq_run_work_fn()函数继续用如下命令调试stap --all-modules  -ve probe kernel.function(blk_mq_run_work_fn) {if(tid() 497) {printf(%s %d\n,execname(),tid()) print_backtrace()}}刷屏打印 kworker/3:1H 497 0xffffffffa4807720 : blk_mq_run_work_fn0x0/0x20 [kernel] 0xffffffffa44d3477 : process_one_work0x1a7/0x360 [kernel] 0xffffffffa44d3b40 : worker_thread0x30/0x390 [kernel] 0xffffffffa44d9502 : kthread0x112/0x130 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] (inexact) 这个打印验证了想法。并且分析可能性最大是__blk_mq_delay_run_hw_queue函数里执行的__blk_mq_run_hw_queue函数。用如下命令验证stap --all-modules  -ve probe kernel.function(__blk_mq_delay_run_hw_queue) {{printf(%s %d\n,execname(),tid()) print_backtrace()}}刷屏打印 kworker/3:1H 497 0xffffffffa4807e20 : __blk_mq_delay_run_hw_queue0x0/0x160 [kernel] 0xffffffffa4807fd8 : blk_mq_delay_run_hw_queues0x38/0x50 [kernel] 0xffffffffa480f412 : blk_mq_do_dispatch_sched0x152/0x160 [kernel] 0xffffffffa480feb9 : __blk_mq_sched_dispatch_requests0x189/0x1e0 [kernel] 0xffffffffa480ff40 : blk_mq_sched_dispatch_requests0x30/0x60 [kernel] 0xffffffffa48076a1 : __blk_mq_run_hw_queue0x51/0xd0 [kernel] 0xffffffffa44d3477 : process_one_work0x1a7/0x360 [kernel] 0xffffffffa44d3b40 : worker_thread0x30/0x390 [kernel] 0xffffffffa44d9502 : kthread0x112/0x130 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] 0xffffffffa4e00255 : ret_from_fork0x35/0x40 [kernel] (inexact) 综合这些调试信息基本可以确定blk_mq_do_dispatch_sched()函数因为派发的rq 是NULL而频繁执行 blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY)-blk_mq_delay_run_hw_queue-__blk_mq_delay_run_hw_queue-kblockd_mod_delayed_work_on(blk_mq_hctx_next_cpu(hctx), hctx-run_work,msecs_to_jiffies(msecs))  而再次触发 mq 异步派发进程就是 kworker/3:1H497 进程。这个逻辑好像没问题但是为什么会频繁触发 blk-mq 异步派发进程 kworker/3:1H 497 呢看下blk_mq_do_dispatch_sched()函数派发IO的代码 static int blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx){    struct request_queue *q hctx-queue;    struct elevator_queue *e q-elevator;    LIST_HEAD(rq_list);    int ret 0;    do {        struct request *rq;        if (e-type-ops.has_work !e-type-ops.has_work(hctx))//bfq_has_work            break;        if (!list_empty_careful(hctx-dispatch)) {            ret -EAGAIN;            break;        }        if (!blk_mq_get_dispatch_budget(hctx))            break;        rq e-type-ops.dispatch_request(hctx);//调用bfq调度器函数 bfq_dispatch_request        if (!rq) {            //如果bfq_dispatch_request返回rq是NULL则执行blk_mq_delay_run_hw_queues()启动blk-mq异步派发IO内核线程            blk_mq_put_dispatch_budget(hctx);            blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY);            break;        }        list_add(rq-queuelist, rq_list);    /*取出rq_list链表上的req派发给磁盘驱动如果因驱动队列繁忙或者nvme硬件繁忙导致派发失败则把rq添加hctx-dispatch等稍后派发遇到rq派发失败返回false退出while循环*/    } while (blk_mq_dispatch_rq_list(q, rq_list, true));    return ret;} 跟踪下bfq_has_work()函数stap --all-modules  -ve probe module(bfq).function(bfq_has_work).return {{printf(%s %d %d bfqd:0x%x\n,execname(),tid(),$return,$hctx-queue-elevator-elevator_data)}}刷屏打印如下 kworker/3:1H 497 1 bfqd:0xffffa0657f07e800kworker/3:1H 497 1 bfqd:0xffffa0657f07e800 是在没什么思路那就把bfq算法核心数据bfqq或bfqd结构体成员信息打印出来看能否发现什么异常启动crash crash bfq_data 0xffffa0657f07e800struct bfq_data {  queue 0xffffa0659740eda8,   dispatch {    next 0xffffa0657f07e808,     prev 0xffffa0657f07e808  }, ........... bfq_high_prio_tmp_list {    next 0xffffa0657f07ec28,     prev 0xffffa0657f07ec28  }, 这两个暂存IO的链表都是空的那bfq_has_work函数返回1只能可能是 bfq_tot_busy_queues 返回true 了测试一下果然是。stap --all-modules  -ve probe module(bfq).function(bfq_tot_busy_queues).return {{printf(%s %d %d\n,execname(),tid(),$return)}}刷屏打印 kworker/3:1H 497 21kworker/3:1H 497 21kworker/3:1H 497 21kworker/3:1H 497 21kworker/3:1H 497 21kworker/3:1H 497 21 此时怀疑有很多IO的派发都有问题。我在内核检测哪些rq添加到bfq算法队列后30s还没传输完成结果打印 [10168.410008] rq:0xffffa0659b96e110 long time do not dispatch[10168.410008] rq:0xffffa0659b95f790 long time do not dispatch[10168.410008] rq:0xffffa0659b950010 long time do not dispatch[10168.410009] rq:0xffffa0659b968350 long time do not dispatch[10168.410009] rq:0xffffa065974b8350 long time do not dispatch[10168.410009] rq:0xffffa0659b958e90 long time do not dispatch[10168.411852] 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0[10168.415764] 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0[10168.419817] 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0[10168.423622] 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0[10168.427652] 5:__bfq_dispatch_request kworker/3:1H 497  dispatch rq:0x0 bfq_high_io_prio_count:0 rq_in_driver:0 有时一个很大的疑问还是重点看下 __bfq_dispatch_request 函数为什么派发的rq总是0把怀疑 里边返回的 bfq_select_queue 有问题。因为__bfq_dispatch_request函数中是先执行bfq_select_queue选择一个bfqq再从bfqq中跳一个rq派发是否bfq_select_queue选择的bfqq就有问题呢当有很多怀疑点时就抓住核心的疑问穷追不舍 用stap --all-modules  -ve probe module(bfq).function(bfq_select_queue).return {{printf(%s %d %d\n,execname(),tid(),$return)}}这个命令调试打印 kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0kworker/3:1H 497 0 果然 bfq_select_queue 返回的bfqq 有问题。那就通过bfqd-in_service_queue看下当前正在派发IO的bfqq是哪个前文调试已经知道bfqd指针是0xffffa0657f07e800。 crash bfq_data 0xffffa0657f07e800 | grep in_service_queue  in_service_queue 0xffffa06597e1c000, crash bfq_queue 0xffffa06597e1c000 | grep pid  pid 1272, crash bt 1272PID: 1272   TASK: ffffa065a692df00  CPU: 0   COMMAND: jbd2/sdb-8 #0 [ffffbcc1c21efa48] __schedule at ffffffffa4cc8826 #1 [ffffbcc1c21efae0] schedule at ffffffffa4cc8cb8 #2 [ffffbcc1c21efaf0] io_schedule at ffffffffa4cc90d2 #3 [ffffbcc1c21efb00] blk_mq_get_tag at ffffffffa480dca9 #4 [ffffbcc1c21efb78] __blk_mq_alloc_request at ffffffffa4807ba1 #5 [ffffbcc1c21efb98] blk_mq_make_request at ffffffffa480ab5e #6 [ffffbcc1c21efc28] generic_make_request at ffffffffa47fe85f #7 [ffffbcc1c21efc80] submit_bio at ffffffffa47feadc #8 [ffffbcc1c21efcc0] submit_bh_wbc at ffffffffa471673a #9 [ffffbcc1c21efcf8] jbd2_journal_commit_transaction at ffffffffc06e28a4 [jbd2]#10 [ffffbcc1c21efea0] kjournald2 at ffffffffc06e792d [jbd2]#11 [ffffbcc1c21eff10] kthread at ffffffffa44d9502#12 [ffffbcc1c21eff50] ret_from_fork at ffffffffa4e00255 当前正在派发rq的bfqq的进程竟然卡死了继续看下bfq_select_queue函数里有哪些疑问看下他的函数源码 static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd){    ................    if (bfq_bfqq_wait_request(bfqq) ||        (bfqq-dispatched ! 0 bfq_better_to_idle(bfqq))) {        ..........        //如果进程有异步bfqq则取出这个异步bfqq        if (async_bfqq             icq_to_bic(async_bfqq-next_rq-elv.icq) bfqq-bic             bfq_serv_to_charge(async_bfqq-next_rq, async_bfqq)             bfq_bfqq_budget_left(async_bfqq))            bfqq bfqq-bic-bfqq[0];        else if (bfq_bfqq_has_waker(bfqq)                bfq_bfqq_busy(bfqq-waker_bfqq)                bfqq-next_rq                bfq_serv_to_charge(bfqq-waker_bfqq-next_rq,                          bfqq-waker_bfqq)                bfq_bfqq_budget_left(bfqq-waker_bfqq)            )            //取出bfqq-waker_bfqq            bfqq bfqq-waker_bfqq;              //bfqd-in_service_queue这个bfqq绑定的进程空闲时没有大量连续快速向bfqq-sort_list插入IO请求特性        else if (!idling_boosts_thr_without_issues(bfqd, bfqq)               //bfqd-in_service_queue这个bfqq没有权重提升             (bfqq-wr_coeff 1 || bfqd-wr_busy_queues 1 ||            //bfqd-in_service_queue这个bfqq绑定的进程在派发IO请求时没有快速插入IO请求的特性              !bfq_bfqq_has_short_ttime(bfqq)))            /*该if成立说明bfqd-in_service_queue这个bfqq初步符合被inject bfqq抢占的条件在bfq_choose_bfqq_for_injection()里如果遍历st-active tree上的bfqq符合bfqd-rq_in_driver limit条件就返回这个bfqq抢占bfqd-in_service_queue*/            bfqq bfq_choose_bfqq_for_injection(bfqd);        else            bfqq NULL;        goto keep_queue;    }expire:     //bfqq过期失效    bfq_bfqq_expire(bfqd, bfqq, false, reason);new_queue:    bfqq bfq_set_in_service_queue(bfqd);    if (bfqq) {        //找到bfqq则goto check_queue分支        goto check_queue;    }keep_queue:    return bfqq;} 用stap --all-modules  -ve probe module(bfq).function(bfq_bfqq_expire) {{printf(%s %d 0x%x\n,execname(),tid(),$bfqq)}}看下是否执行了bfq_bfqq_expire()函数什么打印都没有。再用stap --all-modules  -ve probe module(bfq).function(idling_boosts_thr_without_issues).return {{printf(%s %d 0x%x\n,execname(),tid(),$return)}}看下是否调用了idling_boosts_thr_without_issues函数刷屏打印 kworker/3:1H 497 0x0kworker/3:1H 497 0x0kworker/3:1H 497 0x0kworker/3:1H 497 0x0kworker/3:1H 497 0x0 看来执行到了if (!idling_boosts_thr_without_issues(bfqd, bfqq)…)那个if判断我认为这个if不成立而是执行了else分支bfqq NULL然后goto keep_queue返回bfqq NULL这样就导致bfq_select_queue()函数一直返回NULL呀。怎么验证启动crash工具前文知道当前的bfqq指针是0xffffa06597e1c000 crash bfq_queue 0xffffa06597e1c000 | grep wr_coeff  wr_coeff 30, crash bfq_data 0xffffa0657f07e800 | grep wr_busy_queues  wr_busy_queues 1,  crash bfq_queue 0xffffa06597e1c000 -x | grep flags  flags 0xf2, crash bfq_queue 0xffffa06597e1c000 | grep dispatched  dispatched 2, //bfqq BFQQF_has_short_ttime 是bit5 ,而现在 bfqq:0xffffa06597e1c000 的 flags bit5是1因此 if(!idling_boosts_thr_without_issues(bfqd, bfqq) (bfqq-wr_coeff 1 || bfqd-wr_busy_queues 1 ||!bfq_bfqq_has_short_ttime(bfqq))) 不成立因此 否else 分支 bfqqNULL这就是 bfq_select_queue 返回的bfqq是NULL。神奇了为什么会这样呢 难道我在bfqq添加的代码影响到了 bfqq 算法那段代码要成立得先有更外边的 if (bfq_bfqq_wait_request(bfqq) || (bfqq-dispatched ! 0 bfq_better_to_idle(bfqq))) 成立而BFQQF_wait_request 是bit2但bfqq的flags的bit2是0。bfqq-dispatched 是2那应该是这个导致if ((bfqq-dispatched ! 0 bfq_better_to_idle(bfqq))) 成立。 验证一下 bfq_better_to_idle()返回truestap --all-modules  -ve probe module(bfq).function(bfq_better_to_idle).return {{printf(%s %d bfqq:0x%x 0x%x\n,execname(),tid(),$bfqq,$return)}}刷屏打印 kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1kworker/3:1H 497 bfqq:0xffffa06597e1c000 0x1 看来如果 bfqq:0xffffa06597e1c000 的 dispatched 是0那if就不会成立了吗。但事实是bfqq-dispatched 始终是2 看来问题的根源是 bfqq:0xffffa06597e1c000 的 dispatched 始终是2大于0 呀神奇了难道 我的代码导致 bfqq:0xffffa06597e1c000 的 dispatched 泄漏了导致始终大于0仔细分析我在__bfq_dispatch_request()中添加的代码果然发现了问题如下红色代码 static struct request *__bfq_dispatch_request(struct blk_mq_hw_ctx *hctx){    ....................    rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq);       if (rq) {            if(bfqd-queue-high_io_prio_enable)            {                    if(rq-rq_flags RQF_HIGH_PRIO){//高优先级IO                        if(bfqd-bfq_high_io_prio_mode 0){                            bfqd-bfq_high_io_prio_mode 1;                            hrtimer_start(bfqd-bfq_high_prio_timer, ms_to_ktime(5000),HRTIMER_MODE_REL);                        }                    }                    else//非高优先级IO                    {                       if(bfqd-bfq_high_io_prio_mode)                       {                           //在 bfq_high_io_prio_mode 非0时间的5s内如果遇到非high prio io并且驱动队列IO个数大于限制则把不派发该IO而是临时添加到bfq_high_prio_tmp_list链表                           if(bfqd-rq_in_driver HIGH_PRIO_IO_LIMIT){                                list_add_tail(rq-queuelist,bfqd-bfq_high_prio_tmp_list);                                bfqq-dispatched --;                                bfqd-bfq_high_io_prio_count ;                                return NULL;                           }                       }                    }           }       if(list_empty(bfqd-bfq_high_prio_tmp_list)){inc_in_driver_start_rq:            bfqd-rq_in_driver;start_rq:            rq-rq_flags | RQF_STARTED;        }    }exit:    //1:如果是高优先级IO该if不成立直接跳过。 2:如果非高优先级IO则把rq添加到bfq_high_prio_tmp_list尾从链表头选一个rq派发 3:如果rq是NULL则也从bfq_high_prio_tmp_list选一个rq派发    if(((rq !(rq-rq_flags RQF_HIGH_PRIO)) || !rq)){       if(!list_empty(bfqd-bfq_high_prio_tmp_list)){             if(rq){                 list_add_tail(rq-queuelist,bfqd-bfq_high_prio_tmp_list);                 bfqq-dispatched --;                 bfqd-bfq_high_io_prio_count ;             }             rq list_first_entry(bfqd-bfq_high_prio_tmp_list, struct request, queuelist);              list_del_init(rq-queuelist);             bfqd-bfq_high_io_prio_count --;             bfqq RQ_BFQQ(rq);             if(bfqq)                 bfqq-dispatched;             bfqd-rq_in_driver;             rq-rq_flags | RQF_STARTED;        }    }    return rq;} 如果rq有 RQF_HIGH_PRIO属性rq在派发时先有__bfq_dispatch_request-bfq_dispatch_rq_from_bfqq()默认的bfqq-dispatched。回到__bfq_dispatch_request函数如果 bfq_high_prio_tmp_list 链表空那if(!list_empty(bfqd-bfq_high_prio_tmp_list))不成立就不会执行 rq-rq_flags | RQF_STARTED 。再下边的 if(((rq !(rq-rq_flags RQF_HIGH_PRIO)) || !rq)) 也不成立。于是再次错过了rq-rq_flags | RQF_STARTED。 等rq传输完成执行到bfq_finish_requeue_request函数 static void bfq_finish_requeue_request(struct request *rq){    //由传输完成的IO请求rq得到bfqq    struct bfq_queue *bfqq RQ_BFQQ(rq);    struct bfq_data *bfqd;        if (likely(rq-rq_flags RQF_STARTED)) {        unsigned long flags;        spin_lock_irqsave(bfqd-lock, flags);        if (rq bfqd-waited_rq)            bfq_update_inject_limit(bfqd, bfqq);        //IO传输完成重点执行的函数在这里        bfq_completed_request(bfqq, bfqd);        bfq_finish_requeue_request_body(bfqq);        spin_unlock_irqrestore(bfqd-lock, flags);    }}static void bfq_completed_request(struct bfq_queue *bfqq, struct bfq_data *bfqd){    u64 now_ns;    u32 delta_us;    bfq_update_hw_tag(bfqd);    //已经派发但是还没传输完成的reqIO请求个数    bfqd-rq_in_driver--;    //还没有传输完成的IO请求个数为0表示所有的IO请求都传输完成了跟bfqd-rq_in_driver类似    bfqq-dispatched--;} 因为 rq 没有 RQF_STARTED 标记导致没有执行bfqq-dispatched--这就导致bfqq-dispatched泄漏了。解决方法很简单rq 有 RQF_HIGH_PRIO属性标记并且 bfq_high_prio_tmp_list 链表空时也要执行 rq-rq_flags | RQF_STARTED。把if(list_empty(bfqd-bfq_high_prio_tmp_list))改成if((rq-rq_flags RQF_HIGH_PRIO) || list_empty(bfqd-bfq_high_prio_tmp_list))即可 就是一个细节逻辑分析疏忽导致了这么复杂的排查过程服了 最后关于blk-mq内核派发rq的kworker/0:1H内核线程多了一层理解。blk_mq_do_dispatch_sched函数中因为以后很多个rq暂存在 bfq_high_prio_tmp_list链表 if (e-type-ops.has_work !e-type-ops.has_work(hctx)) 不成立。于是执行 rq e-type-ops.dispatch_request(hctx) 即 __bfq_dispatch_request()。 如果 进程在 执行__bfq_dispatch_request时因为rq没有RQF_HIGH_PRIO属性导致__bfq_dispatch_request返回NULL即 rq e-type-ops.dispatch_request(hctx) 返回NULL那就执行 blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY) 在kworker/0:1H 内核线程延迟派发rq。然后2ms后再次执行 blk_mq_do_dispatch_sched重复上述流程直到bfq_high_prio_tmp_list链表上的rq全派发完。然后bfq_high_prio_tmp_list链表空kworker/0:1H 线程最后一次执行 blk_mq_do_dispatch_sched()bfq_has_work返回NULLif (e-type-ops.has_work !e-type-ops.has_work(hctx)) 成立最终退出rq派发。 相当于我利用了 blk-mq的 blk_mq_delay_run_hw_queues(q, BLK_MQ_BUDGET_DELAY) 延迟派发的特性从而保证没有进程执行 __blk_mq_sched_dispatch_requests-blk_mq_do_dispatch_sched-blk_mq_dispatch_rq_list 派发rq时也可以由内核线程 kworker/0:1H  延迟派发完所有的rq。这样我就不用担心rq暂存到bfq_high_prio_tmp_list链表后会导致这些rq无法被进程主动派发了 3bfqq-dispatched和rq暂存bfq_high_prio_tmp_list链表的深入分析 我在bfq添加的代码有多处 执行 bfqq-dispatched -- 和 bfqq-dispatched 。本身rq在rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 里已经执行 bfqq-dispatched 。我在bfq添加的 bfqq-dispatched -- 和 bfqq-dispatched 是否会影响bfq算法呢我原本的意思是rq如果 添加到 bfq_high_prio_tmp_list链表那就bfqq-dispatched --等rq真正派发时再 bfqq-dispatched 。但是这样有问题如果rq在bfq_high_prio_tmp_list链表停留时间过长因为提前 bfqq-dispatched --如果这是bfqq的最后一个rq就相当于bfqq的所有rq全派发完成了。 但实际并没有只是rq暂存在 bfq_high_prio_tmp_list链表而已。如果 bfqq-dispatched 是0了那估计会影响bfqq过期失效从st-active tree剔除。这样等该bfqq暂存在 bfq_high_prio_tmp_list链表上的rq终于派发了再 bfqq-dispatched 。这样就有问题了因为该bfqq可能已经被新进程拥有了这样分析我的代码里不应该 bfqq-dispatched 和 bfqq-dispatched --。不对分析错了。因为先有 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 里的 bfqq-dispatched 然后再有我的代码里的 bfqq-dispatched --这就相当于该bfqq上的rq并没有派发呀rq还保存在bfqq上这样bfqq也不会过期失效的 但是我的bfq代码是否可以放到 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq); 前边呢因为 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 执行过后相当于rq就从bfqq上的链表剔除了而我把该rq长时间保存在 bfq_high_prio_tmp_list链表可能会影响bfq算法呀。因为正常 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 执行过的rq很快就会传输成功呀。而我是把rq暂存在bfq_high_prio_tmp_list链表可能要过一段时间才会传输完成。 并且 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 选中要派发的rq一定来自bfqq-next_rq 并且还会执行 bfq_dispatch_rq_from_bfqq-bfq_bfqq_served 把rq传输消耗的配额累加到rq所属bfqq的entity-service然后我把rq添加到bfq_high_prio_tmp_list链表。如果这个bfqq的配额正好消耗光了那bfqq就会过期失效。等从bfq_high_prio_tmp_list链表再取出这个rqrq所属的bfqq已经过期失效了然后的代码里却 bfqq-dispatched 。然后派发给驱动等rq传输完成执行bfq_completed_request()还要 bfqq-dispatched--。这样就会有问题了因为bfqq已经过期失效了 问题来了rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq)从bfqq取出rq然后把rq添加到bfq_high_prio_tmp_list链表后rq和原属的bfqq要不要彻底脱离关系???不脱离关系那rq在bfq_high_prio_tmp_list链表暂存时bfqq可能因配额消耗光而失效。这样从bfq_high_prio_tmp_list链表取出该rq后使用rq的bfqq已经过期失效了不能再按照原流程处理了那怎么办rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq)从bfqq取出rq然后把rq添加到bfq_high_prio_tmp_list链表后先执行bfqq-dispatched--这制造一个假象这个rq传输完成了因为正常bfqq-dispatched--就说明rq传输完成了。然后执行 rq-elv.priv[0] NULL 和 rq-elv.priv[1] NULL 令rq所属的bfqq是NULL这样rq和bfqq就脱离关系了接着从bfq_high_prio_tmp_list链表取出该rq后不再执行bfqq-dispatched因为rq不再属于哪个 bfqq了接着派发该rq。然后在该rq传输完成后执行bfq_finish_requeue_request()函数因rq所属bfqq是NULL则直接返回不会再执行bfq_completed_request()令bfqq-dispatched--了。 但是这个方案也有一个问题因为正常情况rq传输完成后会执行 bfq_finish_requeue_request-bfq_completed_request()更新很多bfqq参数这些与bfq算法紧密相关。而我的bfq优化算法一旦rq加入 bfq_high_prio_tmp_list链表就要令rq所属bfqq是NULL然后rq传输完成后就执行不了 bfq_finish_requeue_request-bfq_completed_request() 了影响了bfqq参数更新肯定会对bfq算法造成影响。左右为难没有一个完美的解法。 不对想来想去还是有解法的执行 rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 后然后执行我添加的bfq代码时把rq添加到bfq_high_prio_tmp_list链表。但是把bfqq-dispatched 和 bfqq-dispatched-- 都去掉其他代码不修改。之后 rq所属bfqq可能过期失效从st-active tree 移动到 st-idle tree。但是该bfqq可能会被完全释放吗不会第一bfqq所属的进程派发的rq还有保存在bfq_high_prio_tmp_list链表进程必须等这些rq派发完才会退出。我之前说添加到bfq_high_prio_tmp_list链表的rq的bfqq可能被释放bfqq会被新的进程有用这个说法是错误的。什么情况下bfqq才会被释放呢 在 bfq_put_queue()函数释放bfqq但是前提是 bfqq-ref 是0。每向bfqq插入一个rq则bfqq-ref 看来只有bfqq上的rq全派发完才有可能 bfqq-ref是0。然后才有概率 bfq_forget_entity()- bfq_put_queue()中因 bfqq-ref为0 而释放掉 bfqq。因此即便 bfqq 的rq有插入 bfq_high_prio_tmp_list 链表的然后bfqq上的rq全派发完了bfqq过期失效也不会释放bfqq。应该是这样 因此我的分析把 rq bfq_dispatch_rq_from_bfqq(里边有bfqq-dispatched )上的rq插入bfq_high_prio_tmp_list链表后不再 bfqq-dispatched--就相当于该rq还是属于bfqq只不过换了一个保存位置而已。只不过延迟派发给驱动而已。想想即便没有我的代码rq bfq_dispatch_rq_from_bfqq(bfqd, bfqq) 选中的rq直接派发给驱动在磁盘阵列驱动繁忙时rq也是暂存在磁盘驱动队列这个rq也无法直接派发给磁盘硬件。rq暂存在磁盘驱动队列我的bfq代码是把rq暂存在 bfq_high_prio_tmp_list 链表都是延迟派发有什么区别呢
http://www.zqtcl.cn/news/883190/

相关文章:

  • 电子商务网站建设效果那个网站可以做链接
  • 怎样做投资与理财网站网页设计优秀案例分析
  • 网站制作需要学什么搜狗网页版入口
  • html源码网seo搜索优化工程师招聘
  • 做的网站在小窗口中怎么保持中间广东省公共资源交易中心地址
  • 合肥做网站汇站网织梦网站广告代码教程
  • 复兴专业做网站wordpress搬家502
  • 代做毕网站淘宝权重查询
  • 有专做高端折扣女装的网站吗大连最好的做网站的公司
  • 网站需求嘉兴seo关键词优化
  • 自己开发微网站上海成品网站
  • 国外对企业网站开发的研究山西住房与城乡建设厅定额网站
  • 国家工信部网站备案postfix wordpress
  • 兴宁电子商务网站建设网站模板在线制作
  • 汕头整站优化营销推广网
  • 云服务器搭建网站教程加盟教育培训机构
  • 建筑网站设置工资单人换了怎么换太原做网站找谁
  • 网站做推广需要什么条件重庆网站推广哪家服务好
  • 怎样做理财网站wordpress做产品页教程视频
  • 官网模板建站塔山双喜北京网站建设兴田德润官网多少
  • 网站优化推广外包深圳专业网站建设定制
  • 网站开发aichengkeji元凤建盏简介
  • 移动端网站怎么制作asp做的网站如何发布
  • 做的网站用户密码在哪里找凡科申请的网站和qq空间一样吗
  • 如何自己做网站发布到服务器上面wordpress没有幻灯片
  • 闽侯县建设局网站企业建设网站例文
  • 家居类企业响应式网站搭建电商系统
  • 临沂哪里做网站比较好中国建设银行企业信息门户网站
  • 低价建网站提高网站订单转化率
  • 家居网站应该怎么做网站seo推广软件