免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123
最近访问板块 发新帖
楼主: guotie

内核netfilter处理问题(暨packet接受与NAPI介绍) [复制链接]

论坛徽章:
0
发表于 2006-01-11 17:01 |显示全部楼层
他分析说什么拉。有说do_softirq一定不被硬中断调用的吗

论坛徽章:
0
发表于 2006-01-11 17:43 |显示全部楼层
那个分析是针对2.4的内核的,2.6的内核中softirq的处理变化较大。

正在研读

论坛徽章:
0
发表于 2006-01-11 18:48 |显示全部楼层
找了一个比较老的内核: 2.6.10,我们从头到尾梳理一边, 有问题欢迎指正。

1. 触发硬件中断,进入do_irq()

        fastcall unsigned int do_IRQ(struct pt_regs *regs)
        {       
                irq_enter();
        //调试代码
        ......
        /* 4k栈代码 */
        ......
        //中断实际处理,这时,全局中断是关闭的
                        __do_IRQ(irq, regs);

                irq_exit();

                return 1;
        }

在__do_IRQ()中作以下几件事情

1. ack函数回应硬件中断。
2.handle_IRQ_event() 操作中断号对应的函数链表,执行中断函数,链表上的函数每个分别执行一遍,如果中断源是共享式的,打开全局中断,这时中断可以嵌套。函数退出之前关闭全局中断。

注意,即使上述的全局中断打开,只是CPU中的中断屏蔽位去掉了,但是当前中断号已经锁定。
3. 放开当前中断号锁定。 desc->handler->end(irq)。

所以,由上述可见,到irq_exit()的时候,全局中断锁依然是关闭的。也就是说,中断嵌套只能发生在步骤2中的部分空间中。

irq_exit() -->
void irq_exit(void)
{
/* 标志当前中断已经结束 */
        preempt_count() -= IRQ_EXIT_OFFSET;
        /* 如果依然有中断在处理,如中断嵌套,是不会进入softirq的 */
        /* 如果有软中断已经触发,如网卡收到报文后,在网卡的中断处理函数中,会标记有任务要在软中断中完成 */
        if (!in_interrupt() && local_softirq_pending())
       
        /* 进入软中断 */
                do_softirq();
        /* 标示当前任务可抢占了,当前任务是被中断打断的任务 */
        preempt_enable_no_resched();
}

注意:在上述过程中,CPU全局中断位始终是关闭的。在do_softirq中才放开。如果不进入软中断,进入entry.S 中的ret_from_interrupt,会恢复CPU寄存器以前的值,这样CPU全局中断也会自动打开。

总结,进入do_softirq()之前,全局中断依然是关闭的!!

i386有自己的do_softirq()
asmlinkage void do_softirq(void)
{
        unsigned long flags;
        struct thread_info *curctx;
        union irq_ctx *irqctx;
        u32 *isp;

/* 如果thread_info结构中的preempt_count显示依然在中断或者软中断中,退出。注意,这个和CPU中的sti不一样! */
        if (in_interrupt())
                return;
/* 保存CPU的中断位信息 */
        local_irq_save(flags);

/* 使用软中断自己的栈 */
        if (local_softirq_pending()) {
                curctx = current_thread_info();
                irqctx = softirq_ctx[smp_processor_id()];
                irqctx->tinfo.task = curctx->task;
                irqctx->tinfo.previous_esp = current_stack_pointer;

                /* build the stack frame on the softirq stack */
                isp = (u32*) ((char*)irqctx + sizeof(*irqctx));

                asm volatile(
                        "       xchgl   %%ebx,%%esp     \n"
                                  /* 调用实际处理函数 */
                        "       call    __do_softirq    \n"
                        "       movl    %%ebx,%%esp     \n"
                        : "=b"(isp)
                        : "0"(isp)
                        : "memory", "cc", "edx", "ecx", "eax"
                );
        }

        local_irq_restore(flags);
}


在__do_softirq中
asmlinkage void __do_softirq(void)
{
......
/* 记录当前有多少软中断要处理 */
        pending = local_softirq_pending();

/* 软中断disable*/
        local_bh_disable();
        cpu = smp_processor_id();
restart:
        /* Reset the pending bitmask before enabling irqs */
        local_softirq_pending() = 0;

/* 开全局中断,注意,在这里中断才可以开始抢 */
        local_irq_enable();

/* 软中断向量表 */
        h = softirq_vec;

/* 轮询处理软中断,注意这时是开中断运行的,而且使用了RCU */
        do {
                if (pending & 1) {
                        h->action(h);
                        rcu_bh_qsctr_inc(cpu);
                }
                h++;
                pending >>= 1;
        } while (pending);
/* 关中断 */
        local_irq_disable();

/* 获得处理一轮后,看是否又有软中断触发。如:在数据网卡数据流量很大的情况下,
   开中断的过程中,中断被触发,并在中断函数中设置了软中断 参考8139网卡的cp_interrrupt() */
        pending = local_softirq_pending();

/* 重复上面的过程,上面的默认最多重复10次 */
        if (pending && --max_restart)
                goto restart;

/* 如果继续有软中断,网络情况下,只能说是流量很大。为了保证系统实时行,如2.4内核中,如果网卡流量很大,用户空间的shell敲键盘没有反映。唤醒软中断任务softirqd继续处理报文,注意softirqd要等到irq_exit()退出,并退出到entry.S ret_from_interrupt, 才可能被重新调度执行,记得上面的 preempt_enable_no_resched() 吗? */
        if (pending)
                wakeup_softirqd();

        __local_bh_enable();
}

softirqd每个CPU有一个,任务运行在ksoftirqd()中,在这个任务上下文中,处理软中断,所以,报文的接收net_rx_action()可能运行在ksoftirqd空间,运行在ksoftirqd的内核空间中,但是注意:在处理一轮软中断过程中,这个任务不能被普通任务抢占,中断可以抢占。内核同步就要考虑了。这个有时间详细分析,比较复杂。

在Ingo Molnar 的Realtime补丁中,中断和每个软中断任务都已经线程化,所以中断也可能被实时任务抢占,这对内核编程确实是一大考验。

[ 本帖最后由 xiaozhaoz 于 2006-1-11 19:02 编辑 ]

论坛徽章:
0
发表于 2006-01-11 18:58 |显示全部楼层
但是注意:在处理一轮软中断过程中,这个任务不能抢占。

==========================

好像可以被硬中断抢占吧?
        local_irq_enable();
这个是开中断的。

软中断的action执行过程中,对于网卡的接受数据,也就是netif_rx_action中,可能会产生新的接收硬中断,在这一次软中断处理结束后,goto restart继续处理除非超过最大软中断处理次数!

论坛徽章:
0
发表于 2006-01-11 19:04 |显示全部楼层
Realtime 内核的任务显示:
中断和软中断任务已经线程化

cat /proc/version
Linux version 2.6.14-rt1

top显示:

18:57:03  up 66 days,  9:37,  4 users,  load average: 0.08, 0.03, 0.00
117 processes: 116 sleeping, 1 running, 0 zombie, 0 stopped
CPU states:   1.9% user   3.3% system   0.0% nice   0.0% iowait  94.6% idle
Mem:   255240k av,  246584k used,    8656k free,       0k shrd,   38792k buff
       134240k active,              65432k inactive
Swap:  522072k av,       0k used,  522072k free                   83872k cached

  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME CPU COMMAND
  748 root     18446744073709551570  -5     0    0     0 SW<   0.9  0.0 407:22   0 IRQ 14
  776 root     18446744073709551574  -5     0    0     0 SW<   0.3  0.0  13:24   0 IRQ 16
    5 root     18446744073709551614   0     0    0     0 SW    0.1  0.0  24:28   0 softirq-net-rx/
    1 root      16   0  1452  480   424 S     0.0  0.1   0:01   0 init
    2 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   0:00   0 softirq-high/0
    3 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   0:06   0 softirq-timer/0
    4 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   0:08   0 softirq-net-tx/
    6 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   0:00   0 softirq-scsi/0
    7 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   0:02   0 softirq-tasklet
    8 root     18446744073709551614   0     0    0     0 SW    0.0  0.0   9:57   0 softirq-ktimer/
   12 root      10  -5     0    0     0 SW<   0.0  0.0   0:00   0 khelper
   13 root      11  -5     0    0     0 SW<   0.0  0.0   0:00   0 kthread
   15 root      20  -5     0    0     0 SW<   0.0  0.0   0:00   0 kacpid
   16 root     18446744073709551566  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 9
  715 root     18446744073709551567  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 8
  721 root     18446744073709551568  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 12
  724 root     18446744073709551569  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 6
  749 root     18446744073709551571  -5     0    0     0 SW<   0.0  0.0   0:02   0 IRQ 15
  765 root     18446744073709551572  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 17
  771 root     18446744073709551573  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 18
  781 root     18446744073709551575  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 19
  790 root     18446744073709551576  -5     0    0     0 SW<   0.0  0.0   0:00   0 IRQ 1

论坛徽章:
0
发表于 2006-01-12 08:52 |显示全部楼层
高人!佩服!

我对softirq的分析,不如xiaozhaoz 的详细、准确,也贴出来献丑。

Softirq.c

/*
* We restart softirq processing MAX_SOFTIRQ_RESTART times,
* and we fall back to softirqd after that.
*
* This number has been established via experimentation.
* The two things to balance is latency against fairness -
* we want to handle softirqs as soon as possible, but they
* should not be able to lock up the box.
*/
#define MAX_SOFTIRQ_RESTART 10

asmlinkage void __do_softirq(void)
{
        struct softirq_action *h;
        __u32 pending;
        int max_restart = MAX_SOFTIRQ_RESTART;
        int cpu;

        pending = local_softirq_pending();                //        保存当前softirq状态,即有那些softirq需要处理;

        local_bh_disable();
        cpu = smp_processor_id();
restart:
        /* Reset the pending bitmask before enabling irqs */
        local_softirq_pending() = 0;

        local_irq_enable();

        h = softirq_vec;

        do {
                if (pending & 1) {
                        h->action(h);
                        rcu_bh_qsctr_inc(cpu);
                }
                h++;
                pending >>= 1;
        } while (pending);                //依次处理softirq,直到没有为止。
/*
1.        在处理softirq的动作时,中断是使能的,所以在此过程中,是有可能被硬中断中断的,但不可能被软中断中断,因为do_softirq开始时有判断,该软中断有可能在下面的判断中跳转到restart重新开始;
2.        软中断一次最多处理10次,MAX_SOFTIRQ_RESTART定义,每次最多可以处理32个软中断,不过目前软中断只定义了4类;
*/
        local_irq_disable();

        pending = local_softirq_pending();       
//如果在上面处理软中断的过程中,产生了新的需要处理的软中断,并且没有达到最大的软中断处理次数,返回再次进行处理!
        if (pending && --max_restart)
                goto restart;
//如果达到了最大次数,但是还有未处理完成的软中断,由系统分配完成何事处理!
        if (pending)
                wakeup_softirqd();

        __local_bh_enable();
}
#ifndef __ARCH_HAS_DO_SOFTIRQ

asmlinkage void do_softirq(void)
{
        __u32 pending;
        unsigned long flags;

        if (in_interrupt())                //        这个函数包括软中断和硬中断
                return;

        local_irq_save(flags);                //        保存状态,禁止中断

        pending = local_softirq_pending();                //是否有softirq需要处理

        if (pending)
                __do_softirq();                //        执行软中断

        local_irq_restore(flags);                //        恢复状态,使能中断
}

EXPORT_SYMBOL(do_softirq);

#endif

/* SoftIRQ primitives.  */
#define local_bh_disable() \
                do { add_preempt_count(SOFTIRQ_OFFSET); barrier(); } while (0)
#define __local_bh_enable() \
                do { barrier(); sub_preempt_count(SOFTIRQ_OFFSET); } while (0)

#ifdef CONFIG_DEBUG_PREEMPT
  extern void fastcall add_preempt_count(int val);
  extern void fastcall sub_preempt_count(int val);
#else
# define add_preempt_count(val)        do { preempt_count() += (val); } while (0)
# define sub_preempt_count(val)        do { preempt_count() -= (val); } while (0)
#endif


#define PREEMPT_OFFSET        (1UL << PREEMPT_SHIFT)
#define SOFTIRQ_OFFSET        (1UL << SOFTIRQ_SHIFT)                //0x0100
#define HARDIRQ_OFFSET        (1UL << HARDIRQ_SHIFT)                //0x010000

#define PREEMPT_SHIFT        0
#define SOFTIRQ_SHIFT        (PREEMPT_SHIFT + PREEMPT_BITS)  // 8
#define HARDIRQ_SHIFT        (SOFTIRQ_SHIFT + SOFTIRQ_BITS)  // 16

#define PREEMPT_BITS        8
#define SOFTIRQ_BITS        8

/*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x0fff0000
*/


下面的汇编不明白什么意思,不过从文字上理解应该是得到当前CPU线程的preempt_count值;所以,local_bh_disable其实就是把当前CPU线程的preempt_count加SOFTIRQ_OFFSET(即0x0100),barrier()应该保证执行顺序不被打乱(不确定)。
问题:为什么这样就可以禁止bh了吗?

#define preempt_count()        (current_thread_info()->preempt_count)
/* how to get the thread information struct from C */
static inline struct thread_info *current_thread_info(void)
{
        struct thread_info *ti;
        __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
        return ti;
}


kernel 2.6.13

论坛徽章:
0
发表于 2006-01-12 10:06 |显示全部楼层
do_softirq()中有两个非常值得注意的地方:

asmlinkage void do_softirq(void)
{
/* 如果当前CPU指令运行于中断或者软中断文境中,必须直接返回。
   这可以回答guotie的问题, local_bh_disable()的实际作用是标志当前任务_互斥_软中断。
   1. 如果软中断文境下,任务在__do_softirq()函数时,被中断打断,进入中断处理,中断执行完后,再次进入软中断,到达这里就会返回。
   2. 还有一个,就是如果在内核任务ksoftirqd文境中执行 do_softirq()的时候,被中断打断后,也不会实质进入软中断,这样可以避免大部分的内核同步问题 */
        if (in_interrupt())
                return;

        local_irq_save(flags);

        if (local_softirq_pending()) {
                curctx = current_thread_info();
                irqctx = softirq_ctx[smp_processor_id()];
                irqctx->tinfo.task = curctx->task;
                irqctx->tinfo.previous_esp = current_stack_pointer;

                /* build the stack frame on the softirq stack */
                isp = (u32*) ((char*)irqctx + sizeof(*irqctx));

                asm volatile(
                        "       xchgl   %%ebx,%%esp     \n"
                        "       call    __do_softirq    \n"
                        "       movl    %%ebx,%%esp     \n"
                        : "=b"(isp)
                        : "0"(isp)
                        : "memory", "cc", "edx", "ecx", "eax"
                );
        }

        local_irq_restore(flags);
}

还有一个是__do_softirq()中的
local_bh_disable(),
用来互斥在当前CPU上的后续软中断。防止软中断嵌套,或者Ksoftirqd防止被软中断抢占,导致共享区破坏。所以ksoftirqd在执行中,是不会进入软中断的。

思一克 提到的net_rx_action()只能在软中断文境中被调用。
可能受到了i386有自己的do_softirq函数的影响,在PPC等很多其他体系的CPU中, 用的是以下的do_softirq()函数,注意和i386的区别

#ifndef __ARCH_HAS_DO_SOFTIRQ

asmlinkage void do_softirq(void)
{
        __u32 pending;
        unsigned long flags;

        if (in_interrupt())
                return;

        local_irq_save(flags);

        pending = local_softirq_pending();

        if (pending)
                __do_softirq();

        local_irq_restore(flags);
}

EXPORT_SYMBOL(do_softirq);

#endif

通过这个函数,可以很明显看出,ksoftirqd内核任务调用do_softirq()后,完全执行了net_rx_action()和后续的ip_rcv()。 这两个函数惟一的不同是i386有自己的softirq栈,仅仅是有自己的栈,没有上下文,上下文沿用任务的。
代码如下:
        if (local_softirq_pending()) {
/* 获得当前任务的thread_info结构 */
                curctx = current_thread_info();
/* 获得softirq栈 */
                irqctx = softirq_ctx[smp_processor_id()];
/* 将softirq栈中的task指针指向当前任务,也就是说,软件上下文依然是当前任务,没有完整的软中断上下文 */
                irqctx->tinfo.task = curctx->task;
/* 保存当前的栈指针 */
                irqctx->tinfo.previous_esp = current_stack_pointer;
/* 指向软中断栈顶,栈的大小可能是4K或者8K */
                     /* build the stack frame on the softirq stack */
                isp = (u32*) ((char*)irqctx + sizeof(*irqctx));

                asm volatile(
                                                /* 切换到新栈上,必须用汇编写,同时保存现有栈地址 */
                        "       xchgl   %%ebx,%%esp     \n"
                        "       call    __do_softirq    \n"
                        "       movl    %%ebx,%%esp     \n"
                        : "=b"(isp)
                        : "0"(isp)
                        : "memory", "cc", "edx", "ecx", "eax"
                );
        }

BTW: 关于memory barrier(),现在的讨论已经很多了,现在很多人都直到为什么要用memory barrier这个,但是很少人知道什么时候用,以及用哪种barrier, memory barrier确实是一个很麻烦的东西,大家可以去看看Paul McKenney 写得RCU论文和一些关于memory的文章,非常清楚。

论坛徽章:
0
发表于 2006-01-12 11:40 |显示全部楼层
to xiaozhaoz,

你分析的很好,还没有来的急细看。

总的来说,我是说ip_rcv只可能被软中断程序do_softirq()调用,不可能有别的。你原来说的有两种。
软中断do_softirq()可以被硬中断函数do_IRQ直接调用(不是间接,如你原来追问我的那个帖子所说)。也可以被softirqd kernel调用。前者情况居多。

继续讨论。

论坛徽章:
0
发表于 2007-04-23 11:07 |显示全部楼层
呵呵,好贴,顶
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP