kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

NMI 看门狗

http://blog.csdn.net/arethe/article/details/6153143

[X86和X86-64体系结构均支持NMI看门狗]

你的系统是不是会经常被锁住(Lock up)?直至解锁,系统不再响应键盘?你希望帮助我们解决类似的问题吗?如果你对所有的问题都回答“yes”,那么此文档正是为你而写。

在很多X86/X86-64结构的硬件上,我们都可以使用一种被称为“看门狗NMI中断”的机制。(NMI:Non Maskable Interrupt. 这种中断即使在系统被锁住时,也能被响应)。这种机制可以被用来调试内核锁住现象。通过周期性地执行NMI中断,内核能够监测到是否有CPU被锁住。当有处理器被锁住时,打印调试信息。

为了使用NMI看门狗,首先需要在内核中支持APIC。对于SMP内核,APIC的相关支持已自动地被编译进内核。对于UP内核,需要在内核配置中使能CONFIG_X86_UP_APIC (Processor type and features -> Local APIC support on uniprocessors) 或 CONFIG_X86_UP_IOAPIC (Processor type and features -> IO-APIC support on uniprocessors)。在没有IO-APIC的单处理器系统中,配置CONFIG_X86_UP_APIC。在有IO-APIC的单处理器系统中,则需配置CONFIG_X86_UP_IOAPIC。[注意:某些与内核调试相关选项可能会禁用NMI看门狗。如:Kernel Stack Meter或Kernel Tracer]。

对于X86-64系统,APIC已被编进内核。

使用本地APIC(nmi_watchdog=2)时,需要占用第一个性能寄存器,因而此寄存器不能再被另作它用(如高精度的性能分析)。Oprofile与perfctr的驱动已自动地禁用了本地APIC的NMI看门狗。

可以通过启动参数“nmi_watchdog=N”使能NMI看门狗。即在lilo.conf的相关项中添加如下语句:

1
  append=”nmi_watchdog=1”

对于具有IO-APIC的SMP与UP机器,设置nmi_watchdog=1。对于没有IO-APIC的UP机器,设置nmi_watchdog=2,但仅在某些处理器上可以起作用。如果有疑问,在用nmi_watchdog=1启动后,再查看/proc/interrupts文件中的NMI项,如果该项为0,那么便用nmi_watchdog=2重新启动,并再次检查NMI项。如果还是0,问题就比较严重了,你的处理器很可能不支持NMI。

“锁住(Lockup)”是指如下的情况:如果系统中的任何一个CPU不能处理周期性的本地时钟中断,并持续5秒钟以上,那么NMI的处理函数将产生一个oops并杀死当前进程。这是一种“可控崩溃”(Controlled Crash,所谓可控,是指发生崩溃时,能够输出内核信息),可以用此机制来调试“锁住”现象。那么,无论什么时候发生“锁住”,5秒钟之后都会自动地输出oops。如果内核没有输出信息,说明此时发生的崩溃过于严重(如:hardware-wise),以至于NMI中断都无法被响应,或者此次崩溃使得内核无法打印信息。

在使用本地APIC时要注意,NMI中断被触发的频率依赖于系统的当前负载。由于缺乏更好的时钟源,本地APIC中的NMI看门狗使用的是“有效周期(Cycle unhalted,这个词的翻译似乎不太确切,如果某位朋友有更佳的建议,请告知在下。)”事件。也许你已经猜到了,当CPU处于halted(空等)状态时,该时钟是不计数的。处理器处于空闲状态的时候,常出现这样的情况。如果你的系统在被锁住时,执行的不是hlt指令,看门狗中断很快就会被触发,因为每个时钟周期都会发生“有效周期”事件。如果不幸,处理器在被锁住时,执行的恰是“hlt”指令,那么“有效周期”事件永远都不会发生,看门狗自然也不会被触发。这是本地APIC看门狗的缺陷,在倒霉的时候,永远不会进行时钟计数。而I/O APIC中的看门狗由于采用外部时钟进行驱动,便不存在这个缺陷。但是,它的NMI频率非常高,会显著地影响系统的性能。

X86的nmi_watchdog在默认情况下是禁用的,因此你需要在系统启动的时候使能它。

在系统运行期间,可以禁用NMI看门狗,只要向文件“/proc/sys/kernel/nmi_watchdog”中写“0”即可。向该文件写“1”,将重新使能看门狗。即使如此,你仍然需要在启动时使用参数“nmi_watchdog=”。

注意:在2.4.2-ac18之前的内核中,X86 SMP平台会无条件地使能NMI-oopser。


www.2cto.com/kf/201311/260704.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//  使能hard lockup探测
//  调用路径:watchdog_enable->watchdog_nmi_enable
//  函数任务:
//      1.初始化hard lockup检测事件
//          2.hard lockup阈值为10s
//      2.向performance monitoring子系统注册hard lockup检测事件
//      3.使能hard lockup检测事件
//  注:
//      performance monitoring,x86中的硬件设备,当cpu clock经过了指定个周期后发出一个NMI中断。
1.1 static int watchdog_nmi_enable(unsigned int cpu)
{
	//hard lockup事件
	struct perf_event_attr *wd_attr;
	struct perf_event *event = per_cpu(watchdog_ev, cpu);
	....
	wd_attr = &wd_hw_attr;
	//hard lockup检测周期,10s
	wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);
	//向performance monitoring注册hard lockup检测事件
	event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
	....
	//使能hard lockup的检测
	per_cpu(watchdog_ev, cpu) = event;
	perf_event_enable(per_cpu(watchdog_ev, cpu));
	return 0;
}
 
//  换算hard lockup检测周期到cpu频率
1.2 u64 hw_nmi_get_sample_period(int watchdog_thresh)
{
	return (u64)(cpu_khz) * 1000 * watchdog_thresh;
}
 
//  hard lockup检测事件发生时的nmi回调函数
//  函数任务:
//      1.判断是否发生了hard lockup
//          1.1 dump hard lockup信息
1.3 static void watchdog_overflow_callback(struct perf_event *event,
	 struct perf_sample_data *data,
	 struct pt_regs *regs)
{
	//判断是否发生hard lockup
	if (is_hardlockup()) {
		int this_cpu = smp_processor_id();
 
		//打印hard lockup信息
		if (hardlockup_panic)
			panic("Watchdog detected hard LOCKUP on cpu %d", this_cpu);
		else
			WARN(1, "Watchdog detected hard LOCKUP on cpu %d", this_cpu);
 
		return;
	}
	return;
}
 
//  判断是否发生hard lockup
//  注:
//      如果时钟中断在指定阈值范围内为运行,核心认为可屏蔽中断被屏蔽时间过长
1.4 static int is_hardlockup(void)
{
	//获取watchdog timer的运行次数
	unsigned long hrint = __this_cpu_read(hrtimer_interrupts);
	//在一个hard lockup检测时间阈值内,如果watchdog timer未运行,说明cpu中断被屏蔽时间超过阈值
	if (__this_cpu_read(hrtimer_interrupts_saved) == hrint)
		return 1;
	//记录watchdog timer运行的次数
	__this_cpu_write(hrtimer_interrupts_saved, hrint);
	return 0;
}
 
//  关闭hard lockup检测机制
//  函数任务:
//      1.向performance monitoring子系统注销hard lockup检测控制块
//      2.清空per-cpu hard lockup检测控制块
//      3.释放hard lock检测控制块
2.1 static void watchdog_nmi_disable(unsigned int cpu)
{
	struct perf_event *event = per_cpu(watchdog_ev, cpu);
	if (event) {
		//向performance monitoring子系统注销hard lockup检测控制块
		perf_event_disable(event);
		//清空per-cpu hard lockup检测控制块
		per_cpu(watchdog_ev, cpu) = NULL;
		//释放hard lock检测控制块
		perf_event_release_kernel(event);
	}
	return;
}

中断,进程

blog.chinaunix.net/uid-20806345-id-3203602.html

中断不是进程,不受内核调度器的管辖。在系统处理进程的过程中,对于某个cpu来说,如果有内部中断或外部中断到来,则切换到中断处理程序,切换首先要将进程由用户态要切到进程的内核态,然后再将cpu切换到中断态,待处理完中断返回进程的内核态,再返回进程的用户态,如果中断时进程刚好处于内核态中不用由用户态切到内核态了。
中断处理时是不分优先级的,处理中断的过程中如果有任意中断到来,都会抢占当前的中断处理过程。所以对于要及时响应的中断,需要通过关中断来屏蔽其他中断。通常所说的中断优先级是指中断控制器端的优先级,当有多个中断触发时,首先选择优先级高的中断发出请求给处理器。中断优先级只是对中断控制器而言的,所有的中断对cpu来说都是一样的,没有优先级高低之分。
关中断是关闭所有的外部可屏蔽中断,和优先级没有关系,如果在某中断处理程序中关中断,则不会被任何可屏蔽中断抢占,但是会被任意的不可屏蔽中断抢占。关中断是中断处理程序可选的。

bbs.chinaunix.net/thread-2306027-1-8.html

软中断做的是一些可延迟的费时间的事,当然不能在中断里执行了。
__do_softirq代码,可以看到在执行可延迟函数第一件事就是开中断。但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。
那么这样的后果就是软中断上下文里的会一直执行下去,直到到达了限定次数,然后唤醒守护进程。
因为软中断切换了栈,不再使用进程上下文,那么如果在软中断上下文直接或简洁调用了shedule,那么只有死翘翘了!!因为schedule调度回来的时候是依赖进程内核栈的thread_info。

内核抢占点之一就是中断返回的时候检查是否可以抢占,检查的内容之一就是preempt_count是否等于0,因为禁用了下半部中断,那么肯定就不会等于0的,所以不会被抢占。也就是说返回的时候不会发生调度。

个人理解 中断上下文 最大的特征 禁掉了某种中断(硬中断和软中断),所以导致 不能阻塞。
softirq 有可能在两种方式下被调用,一是中断处理程序退出时,开放硬件中断之后,会去调用do_softirq()。 do_softirq()会禁掉后半部抢占,并且现在执行流使用的是被中断的进程的栈,所以无法阻塞。
softirq的另一种调用方式是ksoftirq内核线程,同样do_softirq()被调用,后半部中断被禁掉,同样禁止阻塞。
工作队列,可以被任何中断或者软中断中断,运行在进程上下文,有自己的栈,可以阻塞。

看一下__do_softirq()的代码,新的硬中断确实可能触发更高优先级的软中断,但是这个软中断并不会在被中断的软中断之前得到执行,软中断始终是顺序执行的。从代码看来,新一批的软中断,无论优先级多高,也得等到前一批的软中断被处理完成之后才能得到处理。而优先级只能帮助软中断在对应的批次中优先得到处理。

硬中断和软中断

关闭硬中断: spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore
关闭软中断: spin_lock_bh和spin_unlock_bh


netfilter:
有些netfilter hooks可以从系统调用的context到达, 比如socket的send_msg()是可以到达LOCAL_OUT/POST_ROUTING的,
这样,也就是说,在这些情况下操作conntrack链表的时候,是进程上下文,而不是软中断上下文, 因此,是需要关闭bh的。

PRE_ROUTING上的按道理说,它只能从软中断到达,因此只需要spin_lock()就可以了。


http://blog.csdn.net/zhangskd/article/details/21992933

概述

从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。 如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事, 跳到中断处理程序的入口点,进行中断处理。

(1) 硬中断

由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。

(2) 软中断

为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。

(3) 中断嵌套

Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行。

(4) 软中断指令

int是软中断指令。
中断向量表是中断号和中断处理函数地址的对应表。
int n - 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。

(5)硬中断和软中断的区别

软中断是执行中断指令产生的,而硬中断是由外设引发的。
硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
硬中断是可屏蔽的,软中断不可屏蔽。
硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。

开关

(1) 硬中断的开关

简单禁止和激活当前处理器上的本地中断:

1
2
local_irq_disable();
local_irq_enable();

保存本地中断系统状态下的禁止和激活:

1
2
3
unsigned long flags;
local_irq_save(flags);
local_irq_restore(flags);
(2) 软中断的开关

禁止下半部,如softirq、tasklet和workqueue等:

1
2
local_bh_disable();
local_bh_enable();

需要注意的是,禁止下半部时仍然可以被硬中断抢占。

(3) 判断中断状态
1
2
3
#define in_interrupt() (irq_count()) // 是否处于中断状态(硬中断或软中断)
#define in_irq() (hardirq_count()) // 是否处于硬中断
#define in_softirq() (softirq_count()) // 是否处于软中断

硬中断

(1) 注册中断处理函数

注册中断处理函数:

1
2
3
4
5
6
7
8
9
10
11
/** 
 * irq: 要分配的中断号 
 * handler: 要注册的中断处理函数 
 * flags: 标志(一般为0) 
 * name: 设备名(dev->name) 
 * dev: 设备(struct net_device *dev),作为中断处理函数的参数 
 * 成功返回0 
 */  
  
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,   
	const char *name, void *dev);  

中断处理函数本身:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef irqreturn_t (*irq_handler_t) (int, void *);  
  
/** 
 * enum irqreturn 
 * @IRQ_NONE: interrupt was not from this device 
 * @IRQ_HANDLED: interrupt was handled by this device 
 * @IRQ_WAKE_THREAD: handler requests to wake the handler thread 
 */  
enum irqreturn {  
	IRQ_NONE,  
	IRQ_HANDLED,  
	IRQ_WAKE_THREAD,  
};  
typedef enum irqreturn irqreturn_t;  
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)  
(2) 注销中断处理函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 
 * free_irq - free an interrupt allocated with request_irq 
 * @irq: Interrupt line to free 
 * @dev_id: Device identity to free 
 * 
 * Remove an interrupt handler. The handler is removed and if the 
 * interrupt line is no longer in use by any driver it is disabled. 
 * On a shared IRQ the caller must ensure the interrupt is disabled 
 * on the card it drives before calling this function. The function does 
 * not return until any executing interrupts for this IRQ have completed. 
 * This function must not be called from interrupt context. 
 */  
  
void free_irq(unsigned int irq, void *dev_id);  

软中断

(1) 定义

软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。
但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。

软中断由softirq_action结构体表示:

1
2
3
struct softirq_action {  
	void (*action) (struct softirq_action *); /* 软中断的处理函数 */  
};  

目前已注册的软中断有10种,定义为一个全局数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static struct softirq_action softirq_vec[NR_SOFTIRQS];  
  
enum {  
	HI_SOFTIRQ = 0, /* 优先级高的tasklets */  
	TIMER_SOFTIRQ, /* 定时器的下半部 */  
	NET_TX_SOFTIRQ, /* 发送网络数据包 */  
	NET_RX_SOFTIRQ, /* 接收网络数据包 */  
	BLOCK_SOFTIRQ, /* BLOCK装置 */  
	BLOCK_IOPOLL_SOFTIRQ,  
	TASKLET_SOFTIRQ, /* 正常优先级的tasklets */  
	SCHED_SOFTIRQ, /* 调度程序 */  
	HRTIMER_SOFTIRQ, /* 高分辨率定时器 */  
	RCU_SOFTIRQ, /* RCU锁定 */  
	NR_SOFTIRQS /* 10 */  
};  
(2) 注册软中断处理函数
1
2
3
4
5
6
7
8
9
/** 
 * @nr: 软中断的索引号 
 * @action: 软中断的处理函数 
 */  
  
void open_softirq(int nr, void (*action) (struct softirq_action *))  
{  
	softirq_vec[nr].action = action;  
}  

例如:

1
2
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(3) 触发软中断

调用raise_softirq()来触发软中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void raise_softirq(unsigned int nr)  
{  
	unsigned long flags;  
	local_irq_save(flags);  
	raise_softirq_irqoff(nr);  
	local_irq_restore(flags);  
}  
  
/* This function must run with irqs disabled */  
inline void rasie_softirq_irqsoff(unsigned int nr)  
{  
	__raise_softirq_irqoff(nr);  
  
	/* If we're in an interrupt or softirq, we're done 
	 * (this also catches softirq-disabled code). We will 
	 * actually run the softirq once we return from the irq 
	 * or softirq. 
	 * Otherwise we wake up ksoftirqd to make sure we 
	 * schedule the softirq soon. 
	 */  
	if (! in_interrupt()) /* 如果不处于硬中断或软中断 */  
		wakeup_softirqd(void); /* 唤醒ksoftirqd/n进程 */  
}  

Percpu变量irq_cpustat_t中的__softirq_pending是等待处理的软中断的位图,通过设置此变量

即可告诉内核该执行哪些软中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline void __rasie_softirq_irqoff(unsigned int nr)  
{  
	trace_softirq_raise(nr);  
	or_softirq_pending(1UL << nr);  
}  
  
typedef struct {  
	unsigned int __softirq_pending;  
	unsigned int __nmi_count; /* arch dependent */  
} irq_cpustat_t;  
  
irq_cpustat_t irq_stat[];  
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)  
#define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))  
#define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)  

唤醒ksoftirqd内核线程处理软中断。

1
2
3
4
5
6
7
8
static void wakeup_softirqd(void)  
{  
	/* Interrupts are disabled: no need to stop preemption */  
	struct task_struct *tsk = __get_cpu_var(ksoftirqd);  
  
	if (tsk && tsk->state != TASK_RUNNING)  
		wake_up_process(tsk);  
}  

在下列地方,待处理的软中断会被检查和执行:
1. 从一个硬件中断代码处返回时
2. 在ksoftirqd内核线程中
3. 在那些显示检查和执行待处理的软中断的代码中,如网络子系统中

而不管是用什么方法唤起,软中断都要在do_softirq()中执行。如果有待处理的软中断,do_softirq()会循环遍历每一个,调用它们的相应的处理程序。在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。

下面来看下do_softirq()的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);  
}  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* 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. 
 */  
asmlinkage void __do_softirq(void)  
{  
	struct softirq_action *h;  
	__u32 pending;  
	/* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */  
	int max_restart = MAX_SOFTIRQ_RESTART;  
	int cpu;  
  
	pending = local_softirq_pending(); /* 激活的软中断位图 */  
	account_system_vtime(current);  
	/* 本地禁止当前的软中断 */  
	__local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);  
	lockdep_softirq_enter(); /* current->softirq_context++ */  
	cpu = smp_processor_id(); /* 当前cpu编号 */  
  
restart:  
	/* Reset the pending bitmask before enabling irqs */  
	set_softirq_pending(0); /* 重置位图 */  
	local_irq_enable();  
	h = softirq_vec;  
	do {  
		if (pending & 1) {  
			unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */  
			int prev_count = preempt_count();  
			kstat_incr_softirqs_this_cpu(vec_nr);  
  
			trace_softirq_entry(vec_nr);  
			h->action(h); /* 调用软中断的处理函数 */  
			trace_softirq_exit(vec_nr);  
  
			if (unlikely(prev_count != preempt_count())) {  
				printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"  
					"exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,  
					preempt_count());  
			}  
			rcu_bh_qs(cpu);  
		}  
		h++;  
		pending >>= 1;  
	} while(pending);  
  
	local_irq_disable();  
	pending = local_softirq_pending();  
	if (pending & --max_restart) /* 重复触发 */  
		goto restart;  
  
	/* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */  
	if (pending)  
		wakeup_softirqd();   
  
	lockdep_softirq_exit();  
	account_system_vtime(current);  
	__local_bh_enable(SOFTIRQ_OFFSET);  
}  
(4) ksoftirqd内核线程

内核不会立即处理重新触发的软中断。
当大量软中断出现的时候,内核会唤醒一组内核线程来处理。
这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。
但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。

每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static int run_ksoftirqd(void *__bind_cpu)  
{  
	set_current_state(TASK_INTERRUPTIBLE);  
	current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */  
  
	while(! kthread_should_stop()) {  
		preempt_disable();  
  
		if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */  
			preempt_enable_no_resched();  
			schedule();  
			preempt_disable():  
		}  
  
		__set_current_state(TASK_RUNNING);  
  
		while(local_softirq_pending()) {  
			/* Preempt disable stops cpu going offline. 
			 * If already offline, we'll be on wrong CPU: don't process. 
			 */  
			 if (cpu_is_offline(long)__bind_cpu))/* 被要求释放cpu */  
				 goto wait_to_die;  
  
			do_softirq(); /* 软中断的统一处理函数 */  
  
			preempt_enable_no_resched();  
			cond_resched();  
			preempt_disable();  
			rcu_note_context_switch((long)__bind_cpu);  
		}  
  
		preempt_enable();  
		set_current_state(TASK_INTERRUPTIBLE);  
	}  
  
	__set_current_state(TASK_RUNNING);  
	return 0;  
  
wait_to_die:  
	preempt_enable();  
	/* Wait for kthread_stop */  
	set_current_state(TASK_INTERRUPTIBLE);  
	while(! kthread_should_stop()) {  
		schedule();  
		set_current_state(TASK_INTERRUPTIBLE);  
	}  
  
	__set_current_state(TASK_RUNNING);  
	return 0;  
}

关于ksoftirqd进程

blog.chinaunix.net/uid-20737871-id-1881243.html

每个处理器都有一组辅助处理器软中断(和tasklet)的内核线程。当内核中出现大量软中断的时候,这些内核进程就会辅助处理它们。

引入ksoftirq内核线程的原因:

对于软中断,内核会选择在几个特殊时机进行处理。而在中断处理程序返回时处理是最常见的。软中断被触发的频率有时可能很高,更不利的是,处理函数有时还会 字形重复触发,那么就会导致用户空间进程无法获得足够的处理时间,因而处于饥饿状态。单纯的对重新触发的软中断采取不立即处理的策略,也无法让人接受。

最初的解决方案:

1)只要还有被触发并等待处理的软中断,本次执行就要负责处理,重新触发的软中断也在本次执行返回前被处理。这样做可以保证对内核的软中断采取即时处理的 方式,关键在于,对重新触发的软中断也会立即处理。当负载很高的时候,此时若有大量被触发的软中断,而它们本身又会重复触发。系统可能会一直处理软中断根 本不能完成其他任务。

2)不处理重新触发的软中断。在从中断返回的时候,内核和平常一样,也会检查所有挂起的软中断并处理他们。但是,任何自行重新触发的软中断不会马上处理, 它们被放到下一个软中断执行时机去处理。而这个时机通常也就是下一次中断返回的时候。可是,在比较空闲的系统中,立即处理软中断才是比较好的做法。尽管它 能保证用户空间不处于饥饿状态,但它却让软中断忍受饥饿的痛苦,而根本没有好好利用闲置的系统资源。

改进:

最终在内核中实现的方案是不会立即处理处理重新触发的软中断。而作为改进,当大量软中断出现的时候,内核会唤醒一组内核线程来处理这些负载。这些线程在最 低的优先级上运行(nice值是19),这能避免它们跟其他重要的任务抢夺资源。但它们最终肯定会被执行,所以这个折中方案能够保证在软中断负担很中的时 候用户程序不会因为得不到处理时间处于饥饿状态。相应的,也能保证”过量“的软中断终究会得到处理。

每个处理器都有一个这样的线程。所有线程的名字都叫做ksoftirq/n,区别在于n,它对应的是处理器的编号。在一个双CPU的机器上就有两个这样的 线程,分别叫做ksoftirqd/0和ksoftirqd/1。为了保证只要有空闲的处理器,它们就会处理软中断,所以给每个处理器都分配一个这样的线 程。一旦该线程被初始化,它就会执行类似下面这样的死循环:

在kernel/softirq.c中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
static int ksoftirqd(void * __bind_cpu)
{
	set_user_nice(current, 19);
	current->flags |= PF_NOFREEZE;
	set_current_state(TASK_INTERRUPTIBLE);
	while (!kthread_should_stop()) {
		preempt_disable();
		if (!local_softirq_pending()) {
			preempt_enable_no_resched();
			schedule();
			preempt_disable();
		}
		__set_current_state(TASK_RUNNING);
		while (local_softirq_pending()) {
			/* Preempt disable stops cpu going offline.
				If already offline, we'll be on wrong CPU:
				don't process */
			if (cpu_is_offline((long)__bind_cpu))
				goto wait_to_die;
			do_softirq();
			preempt_enable_no_resched();
			cond_resched();
			preempt_disable();
		}
		preempt_enable();
		set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;
wait_to_die:
	preempt_enable();
	/* Wait for kthread_stop */
	set_current_state(TASK_INTERRUPTIBLE);
	while (!kthread_should_stop()) {
		schedule();
		set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);
	return 0;
}

只要有待处理的软中断(由softirq_pending()函数负责发现),ksoftirq就会调用do_softirq去处理它们。通过重复执行这 样的操作,重新触发的软中断也会被执行。如果有必要,每次迭代后都会调用schedule()以便让更重要的进程得到处理机会。当所有需要执行的操作都完 成以后,该内核线程将自己设置为TASK_INTERRUPTIBLE状态,唤起调度程序选择其他可执行进程投入运行。

只要do_softirq()函数发现已经执行过的内核线程重新触发了它自己,软中断内核线程就会被唤醒.

编译I9507V内核

源码地址 http://opensource.samsung.com/reception/receptionSub.do?method=sub&sub=F&searchValue=9507

好像三星android4.3版本后的bootloader会检测是否三星自编译内核,不是的会开机提示一下,不影响正常使用。

按照README_Kernel.txt的做。

内核中说明是用4.7编译器,但是4.7编译出来的装上去会挂,不知道为什么。
但是换成4.6编译器就没问题。

1
make VARIANT_DEFCONFIG=jftdd_eur_defconfig jf_defconfig SELINUX_DEFCONFIG=selinux_defconfig

最后作成boot.img

1
2
3
4
mkbootimg --kernel zImage --ramdisk boot.img-ramdisk.cpio.gz --base 80200000 --ramdisk_offset 1FF8000 --pagesize 2048 --cmdline "console=null androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x3F ehci-hcd.park=3 maxcpus=4" -o boot.img

tar cf boot.img.tar boot.img
用odin3.10,选择AP项刷入

修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
diff --git a/Makefile b/Makefile
index 16603ac..6d2b29f 100755
--- a/Makefile
+++ b/Makefile
@@ -193,7 +193,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
 # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
 export KBUILD_BUILDHOST := $(SUBARCH)
 ARCH     ?=arm
-CROSS_COMPILE    ?=/opt/toolchains/arm-eabi-4.7/bin/arm-eabi-
+CROSS_COMPILE    ?=/home/kk/android/gcc-arm-none-eabi-4_6-2012q4/bin/arm-none-eabi-
 
 # Architecture as present in compile.h
 UTS_MACHINE  := $(ARCH)
diff --git a/arch/arm/configs/jftdd_eur_defconfig b/arch/arm/configs/jftdd_eur_defconfig
index 389a204..2c53b92 100755
--- a/arch/arm/configs/jftdd_eur_defconfig
+++ b/arch/arm/configs/jftdd_eur_defconfig
@@ -2,3 +2,5 @@ CONFIG_MACH_JF_EUR=y
 CONFIG_MACH_JFTDD_EUR=y
 CONFIG_EXTRA_FIRMWARE="audience-es325-fw-eur.bin"
 CONFIG_EXTRA_FIRMWARE_DIR="firmware"
+CONFIG_LOCALVERSION="-chn-kk"
+CONFIG_LOCALVERSION_AUTO=n
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 0525927..4dbc5cf 100755
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -98,7 +98,13 @@ extern void printk_tick(void);
 asmlinkage __printf(1, 0)
 int vprintk(const char *fmt, va_list args);
 asmlinkage __printf(1, 2) __cold
-int printk(const char *fmt, ...);
+
+static __always_inline int printk(const char *fmt, ...)
+{
+ return 0;
+}
+
+int orig_printk(const char *s, ...);
 
 /*
  * Special printk facility for scheduler use only, _DO_NOT_USE_ !
diff --git a/include/net/tcp.h b/include/net/tcp.h
index a70c0e4..0d13553 100755
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -140,9 +140,9 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo);
                                   * for local resources.
                                   */
 
-#define TCP_KEEPALIVE_TIME   (120*60*HZ) /* two hours */
-#define TCP_KEEPALIVE_PROBES 9       /* Max of 9 keepalive probes    */
-#define TCP_KEEPALIVE_INTVL  (75*HZ)
+#define TCP_KEEPALIVE_TIME   (10*60*HZ)  /* 10 mins */
+#define TCP_KEEPALIVE_PROBES 7       /* Max of 7 keepalive probes    */
+#define TCP_KEEPALIVE_INTVL  (60*HZ)
 
 #define MAX_TCP_KEEPIDLE 32767
 #define MAX_TCP_KEEPINTVL    32767
diff --git a/kernel/printk.c b/kernel/printk.c
index f7c85aa..80d2cd0 100755
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -995,7 +995,7 @@ static int have_callable_console(void)
  * See the vsnprintf() documentation for format string extensions over C99.
  */
 
-asmlinkage int printk(const char *fmt, ...)
+asmlinkage int orig_printk(const char *fmt, ...)
 {
  va_list args;
  int r;
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 5f5959c..03ba861 100755
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -6912,7 +6912,7 @@ void __init sched_init_smp(void)
 }
 #endif /* CONFIG_SMP */
 
-const_debug unsigned int sysctl_timer_migration = 1;
+const_debug unsigned int sysctl_timer_migration = 0;
 
 int in_sched_functions(unsigned long addr)
 {
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 10def3a..a68e108 100755
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -75,7 +75,7 @@
 #include <asm/unaligned.h>
 #include <net/netdma.h>
 
-int sysctl_tcp_timestamps __read_mostly = 1;
+int sysctl_tcp_timestamps __read_mostly = 0;
 int sysctl_tcp_window_scaling __read_mostly = 1;
 int sysctl_tcp_sack __read_mostly = 1;
 int sysctl_tcp_fack __read_mostly = 1;
@@ -1300,7 +1300,7 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
          unsigned int new_len = (pkt_len / mss) * mss;
          if (!in_sack && new_len < pkt_len) {
              new_len += mss;
-             if (new_len > skb->len)
+             if (new_len >= skb->len)
                  return 0;
          }
          pkt_len = new_len;
@@ -4782,7 +4782,7 @@ restart:
      int copy = SKB_MAX_ORDER(header, 0);
 
      /* Too big header? This can happen with IPv6. */
-     if (copy < 0)
+     if (copy <= 0)
          return;
      if (end - start < copy)
          copy = end - start;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 3cabafb..6b36c25 100755
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -27,7 +27,7 @@
 #include <net/inet_common.h>
 #include <net/xfrm.h>
 
-int sysctl_tcp_syncookies __read_mostly = 1;
+int sysctl_tcp_syncookies __read_mostly = 0;
 EXPORT_SYMBOL(sysctl_tcp_syncookies);
 
 int sysctl_tcp_abort_on_overflow __read_mostly;
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 72b5088..7c54171 100755
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -50,7 +50,7 @@ static unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] __read_mostly = {
  [SCTP_CONNTRACK_CLOSED]         = 10 SECS,
  [SCTP_CONNTRACK_COOKIE_WAIT]        = 3 SECS,
  [SCTP_CONNTRACK_COOKIE_ECHOED]      = 3 SECS,
- [SCTP_CONNTRACK_ESTABLISHED]        = 5 DAYS,
+ [SCTP_CONNTRACK_ESTABLISHED]        = 20 MINS,
  [SCTP_CONNTRACK_SHUTDOWN_SENT]      = 300 SECS / 1000,
  [SCTP_CONNTRACK_SHUTDOWN_RECD]      = 300 SECS / 1000,
  [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT]  = 3 SECS,
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 0d07a1d..84d55e4 100755
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -32,7 +32,7 @@
 /* "Be conservative in what you do,
     be liberal in what you accept from others."
     If it's non-zero, we mark only out of window RST segments as INVALID. */
-static int nf_ct_tcp_be_liberal __read_mostly = 0;
+static int nf_ct_tcp_be_liberal __read_mostly = 1;
 
 /* If it is set to zero, we disable picking up already established
    connections. */
@@ -67,7 +67,7 @@ static const char *const tcp_conntrack_names[] = {
 static unsigned int tcp_timeouts[TCP_CONNTRACK_TIMEOUT_MAX] __read_mostly = {
  [TCP_CONNTRACK_SYN_SENT]    = 2 MINS,
  [TCP_CONNTRACK_SYN_RECV]    = 60 SECS,
- [TCP_CONNTRACK_ESTABLISHED] = 5 DAYS,
+ [TCP_CONNTRACK_ESTABLISHED] = 20 MINS,
  [TCP_CONNTRACK_FIN_WAIT]    = 2 MINS,
  [TCP_CONNTRACK_CLOSE_WAIT]  = 60 SECS,
  [TCP_CONNTRACK_LAST_ACK]    = 30 SECS,
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 857ea4f..742fe12 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -184,7 +184,7 @@ else
  # LOCALVERSION= is not specified
  if test "${LOCALVERSION+set}" != "set"; then
      scm=$(scm_version --short)
-     res="$res${scm:++}"
+     res="$res${scm:+}"
  fi
 fi