1 使用kthread_create创建线程:
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char *namefmt, ...);
这个函数可以像printk一样传入某种格式的线程名
线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。
2. 当然,还有一个创建并启动线程的函数:kthread_run
struct task_struct *kthread_run(int (*threadfn)(void *data),
void *data,
const char *namefmt, ...);
3. 线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。
int kthread_stop(struct task_struct *thread);
kthread_stop() 通过发送信号给线程。
如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。
参考:Kernel threads made easy
简单的说,就是linux内核为每个下发的scsi磁盘命令加个定时器,如果超时了下面底层驱动还没有处理完这个scsi命令的话,就开始触发scsi_unjam_host开始的动作。这个函数会进行一些逐渐提高等级的恢复操作,然后看你这个底层的scsi host是不是可以恢复到正常状态了。
默认的策略是这样的,不过好像也可以在自己的scsi host里面自定义恢复策略。
scsi_eh_abort_cmds //先是取消这个超时命令,调用scsi host的接口通知下层取消
if (!scsi_eh_stu(shost, work_q, done_q)) //START_STOP_UNIT command 让这个磁盘重新就绪?
if (!scsi_eh_bus_device_reset(shost, work_q, done_q)) //如果还是不行,重启整个磁盘设备
if (!scsi_eh_bus_reset(shost, work_q, done_q)) //如果还是不行,重启整个总线 ,可能影响很多磁盘了
if (!scsi_eh_host_reset(work_q, done_q)) //重启整个host,整个host上面的所有磁盘估计都要重启了。
scsi_eh_offline_sdevs(work_q, done_q); //
这里的scsi错误指的不是 scsi命令返回的错误结果,而是scsi命令被分发给下层驱动之后,驱动超时了也没有返回一个结果回来,就是scsi命令在底层挂住了停止响应了。
以前linux内核会为每个scsi磁盘命令建一个 timer的,使用scsi_add_timer这个函数来作的。 不过后来好像2.6.28开始改了办法了,因为他们发现这样可能导致创建的timer 太多,影响性能吧。作磁盘测试的时候,可以达到每秒3000个磁盘操作,每个超时设为100秒的时候,都会导致创建很多timer了。 应该是把这个timer计时放到block层来做了,每个队列 queue里面所有磁盘指令现在共用一个timer,然后每次执行一个磁盘指令的时候,修改这个timer的超时为所有指令的最小超时时间。而不是为每个指令都建立一个新的timer。
这个新的机制应该是在lwn的这篇文章里面有描述的,不过不知道怎么回事今天lwn在我这里打不开。
Block layer: solid-state storage, timeouts, affinity, and more http://lwn.net/Articles/303270/
timer的超时处理函数最后调到这个scsi_eh_scmd_add函数,就会唤醒scsi的错误恢复进程
82/**
83 * scsi_eh_scmd_add - add scsi cmd to error handling.
84 * @scmd: scmd to run eh on.
85 * @eh_flag: optional SCSI_EH flag.
86 *
87 * Return value:
88 * 0 on failure.
89 */
90int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
91{
92 struct Scsi_Host *shost = scmd->device->host;
93 unsigned long flags;
94 int ret = 0;
95
96 if (!shost->ehandler)
97 return 0;
98
99 spin_lock_irqsave(shost->host_lock, flags);
100 if (scsi_host_set_state(shost, SHOST_RECOVERY))
101 if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
102 goto out_unlock;
103
104 ret = 1;
105 scmd->eh_eflags |= eh_flag;
106 list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
107 shost->host_failed++;
108 scsi_eh_wakeup(shost); 唤醒超时处理线程
109 out_unlock:
110 spin_unlock_irqrestore(shost->host_lock, flags);
111 return ret;
112}
//scsi把这个scsi_times_out函数注册为block层的超时处理函数了。
114/**
115 * scsi_times_out - Timeout function for normal scsi commands.
116 * @req: request that is timing out.
117 *
118 * Notes:
119 * We do not need to lock this. There is the potential for a race
120 * only in that the normal completion handling might run, but if the
121 * normal completion function determines that the timer has already
122 * fired, then it mustn't do anything.
123 */
124enum blk_eh_timer_return scsi_times_out(struct request *req)