fuse处理的请求和操作非常多,代码里有相当多的函数,分析起来很容易陷入函数调用泥潭里,所以要抓住关键分析。
fuse的关键是对请求队列的处理,所以搞清楚fuse如何管理请求队列是基础。先上一张关系图
这里一共有两个进程等待队列:一个是用户空间fuse库对/dev/fuse读写的进程等待队列,一个是发起VFS调用请求的进程的等待队列
一个请求pending队列:需要处理的请求都挂在这个队列上
下面再看相关数据结构
struct fuse_conn {
/** Readers of the connection are waiting on this */
wait_queue_head_t waitq;
/** The list of pending requests */
struct list_head pending;
/** The list of requests being processed */
struct list_head processing;
/** Pending interrupts */
struct list_head interrupts;
......
};
struct fuse_req {
/** This can be on either pending processing or io lists in fuse_conn */
struct list_head list;
/** Entry on the interrupts list */
struct list_head intr_entry;
/** Used to wake up the task waiting for completion of request*/
wait_queue_head_t waitq;
......
};
1. fuse守护进程在fuse_session_loop中一直read设备-->内核中fuse_dev_read-->fuse_dev_do_read-->request_wait
将进程睡眠在fc->waitq上
/* Wait until a request is available on the pending list */
static void request_wait(struct fuse_conn *fc)
__releases(fc->lock)
__acquires(fc->lock)
{
DECLARE_WAITQUEUE(wait, current);
add_wait_queue_exclusive(&fc->waitq, &wait);
while (fc->connected && !request_pending(fc)) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current))
break;
spin_unlock(&fc->lock);
schedule();
spin_lock(&fc->lock);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&fc->waitq, &wait);
}
判断进程是否睡眠的条件是request_pending()
static int request_pending(struct fuse_conn *fc)
{
return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) ||
forget_pending(fc);
}
看三个请求队列是不是为空,为空则睡眠,不为空则处理
2. VFS文件系统操作请求使用fuse_request_send向fuse守护进程发送请求
static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
BUG_ON(req->background);
spin_lock(&fc->lock);
if (!fc->connected)
req->out.h.error = -ENOTCONN;
else if (fc->conn_error)
req->out.h.error = -ECONNREFUSED;
else {
req->in.h.unique = fuse_get_unique(fc);
queue_request(fc, req); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/* acquire extra reference, since request is still needed
after request_end() */
__fuse_get_request(req);
request_wait_answer(fc, req); <<<<<<<<<<<<<<<<<<<<<<<<<<<
}
spin_unlock(&fc->lock);
}
void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
__fuse_request_send(fc, req);
}
EXPORT_SYMBOL_GPL(fuse_request_send);
首先调用queue_request将请求放入fc->pending队列并唤醒用户空间fuse守护进程处理请求,
static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
{
req->in.h.len = sizeof(struct fuse_in_header) +
len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
list_add_tail(&req->list, &fc->pending);
req->state = FUSE_REQ_PENDING;
if (!req->waiting) {
req->waiting = 1;
atomic_inc(&fc->num_waiting);
}
wake_up(&fc->waitq);
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
}
然后调用rquest_wait_answer睡眠在req->waitq上等待处理完成
static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
__releases(fc->lock)
__acquires(fc->lock)
{
... ...
/*
* Either request is already in userspace, or it was forced.
* Wait it out.
*/
spin_unlock(&fc->lock);
wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
spin_lock(&fc->lock);
... ...
}
3. fuse守护进程被唤醒,处理完成后,将结果写回fuse设备,并唤醒req->waitq上等待进程来取回结果,fuse_dev_write-->fuse_dev_do_write
static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct fuse_copy_state cs;
struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp);
if (!fc)
return -EPERM;
fuse_copy_init(&cs, fc, 0, iov, nr_segs);
return fuse_dev_do_write(fc, &cs, iov_length(iov, nr_segs));
}
/*
* Write a single reply to a request. First the header is copied from
* the write buffer. The request is then searched on the processing
* list by the unique ID found in the header. If found, then remove
* it from the list and copy the rest of the buffer to the request.
* The request is finished by calling request_end()
*/
static ssize_t fuse_dev_do_write(struct fuse_conn *fc,
struct fuse_copy_state *cs, size_t nbytes)
{
... ...
req = request_find(fc, oh.unique);
... ...
request_end(fc, req);
... ...
}
4. VFS文件系统操作请求被唤醒,取回处理结果
/*
* This function is called when a request is finished. Either a reply
* has arrived or it was aborted (and not yet sent) or some error
* occurred during communication with userspace, or the device file
* was closed. The requester thread is woken up (if still waiting),
* the 'end' callback is called if given, else the reference to the
* request is released
*
* Called with fc->lock, unlocks it
*/
static void request_end(struct fuse_conn *fc, struct fuse_req *req)
__releases(fc->lock)
{
... ...
req->state = FUSE_REQ_FINISHED;
wake_up(&req->waitq);
... ...
}
下面以一个删除文件操作展示一下具体流程:>为调用,<为返回
到此可以看清fuse和用户空间交互的过程,fuse用户态库和内核代码对请求处理的细节待后续分析。