nfs4_state_owner代表了客户端的一个用户,包含了这个用户打开的所有文件的信息。每个文件用数据结构nfs4_state表示,每打开一个文件就向nfs4_state_owner中添加一个nfs4_state结构。因此,OPEN操作中首先要检查客户端是否为当前用户创建了nfs4_state_owner结构,如果没有就创建一个。这个流程是由函数nfs4_get_state_owner()实现的。
参数server:表示一个NFS文件系统
参数cred:这是用户信息的数据结构,表示客户端的一个用户
参数gfp_flags:这是分配内存时使用的标志位
struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
struct rpc_cred *cred,
gfp_t gfp_flags)
{
struct nfs_client *clp = server->nfs_client; // 找到NFS客户端的数据结构
struct nfs4_state_owner *sp, *new;
spin_lock(&clp->cl_lock);
// 在server->state_owners中查找符合条件的nfs4_state_owner结构,查询条件是cred,每个cred代表一个用户.
// 返回符合条件的nfs4_state_owner结构,并增加该结构的引用计数。
// 如果nfs4_state_owner已经挂载到lru链表中了,就从lru链表中删除.
// 如果不存在符合条件的nfs4_state_owner结构,就返回NULL.
sp = nfs4_find_state_owner_locked(server, cred);
spin_unlock(&clp->cl_lock);
if (sp != NULL) // 找到了,直接退出就可以了.
goto out;
// 当前用户还没有创建nfs4_state_owner结构.为当前用户创建一个新的nfs4_state_owner结构.
// 初始化引用计数为1.
new = nfs4_alloc_state_owner(server, cred, gfp_flags);
if (new == NULL)
goto out; // 创建过程失败,分配内存过程出错了.
do {
// 为新创建的nfs4_state_owner结构分配编号
if (ida_pre_get(&server->openowner_id, gfp_flags) == 0)
break; // 分配编号过程出错了
spin_lock(&clp->cl_lock);
// 将新创建的nfs4_state_owner结构添加到红黑树中,并且设置了编号.
// 由于nfs4_alloc_state_owner()分配内存时可能休眠,这个过程中其他进程可能
// 创建了新的nfs4_state_ower结构,因此nfs4_insert_state_owner_locked()
// 将new添加到红黑树之前又查找了一次,如果找到了符合条件的nfs4_state_owner结构
// 就不添加了,这种情况下sp和new就不相同了.
sp = nfs4_insert_state_owner_locked(new);
spin_unlock(&clp->cl_lock);
} while (sp == ERR_PTR(-EAGAIN));
if (sp != new) // 查找到了符合条件的nfs4_state_owner结构,就可以释放刚刚创建的结构了.
nfs4_free_state_owner(new);
out:
// 检查LRU链表中过期的nfs4_state_owner结构,删除这些结构.
// 当nfs4_state_owner在lru链表中的时间超过了90秒就说明过期了,就需要删除了.
// 90秒内的nfs4_state_owner不允许删除,因为这是刚释放的,有可能还被其他进程使用.
nfs4_gc_state_owners(server);
return sp;
}
NFS文件系统中所有的nfs4_state_owner结构保存在一棵红黑树中,红黑树的根节点是nfs_server结构中的state_owners。暂时不清楚为什么要使用一棵红黑树,可能是为大系统准备的。查找nfs4_state_owner结构的函数是nfs4_find-state_owner_locked(),这个函数将当前用户的信息和红黑树中每个节点中的用户信息进行比较,用户信息相同就说明是同一个用户的结构。
参数server:表示一个NFS文件系统
参数cred:这是用户信息
// 查找符合条件的nfs4_state_owner结构,在nfs_server->state_owners构成的红黑树中进行查找.
// (1) 在红黑树中找到了,增加引用计数,然后返回.
// (2) 在红黑树中找到了,但是也链接到lru链表中了,先从lru链表中删除,然后增加引用计数,返回
// (3) 很可惜,没有找到,直接返回NULL.
static struct nfs4_state_owner *
nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred)
{
// 取出nfs_server结构中保存nfs4_state_owner结构的红黑树
struct rb_node **p = &server->state_owners.rb_node,
*parent = NULL;
struct nfs4_state_owner *sp;
while (*p != NULL) {
parent = *p;
// 取出红黑树节点中关联的nfs4_state_owner结构
sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
// 检查是否是我们查找的节点,检查条件就是用户信息.
if (cred < sp->so_cred)
p = &parent->rb_left;
else if (cred > sp->so_cred)
p = &parent->rb_right;
else { // 相等了,找到当前用户了.
if (!list_empty(&sp->so_lru)) // 这个结构已经链接到lru链表中了
list_del_init(&sp->so_lru); // 先从lru链表中删除,我们要开始使用这个结构了,不需要回收了.
atomic_inc(&sp->so_count); // 增加这个结构的引用计数
return sp; // 这就是我们要使用的nfs4_state_owner结构.
}
}
return NULL; // 可惜了,没有找到.
}
如果在红黑树中没有找到符合条件的nfs4_state_ower结构,就需要创建一个新的nfs4_state_owner结构,创建nfs4_state_owner结构的函数是nfs4_alloc_state_owner()。
参数server:表示一个NFS文件系统
参数cred:这是用户信息的数据结构,表示客户端的一个用户
参数gfp_flags:这是分配内存时使用的标志位
static struct nfs4_state_owner *
nfs4_alloc_state_owner(struct nfs_server *server,
struct rpc_cred *cred,
gfp_t gfp_flags)
{
struct nfs4_state_owner *sp;
sp = kzalloc(sizeof(*sp), gfp_flags); // 分配内存
if (!sp)
return NULL; // 分配内存过程失败,直接返回.
sp->so_server = server; // 所属的nfs_server结构
sp->so_cred = get_rpccred(cred); // 这是用户信息
spin_lock_init(&sp->so_lock); // 保护这个结构的自旋锁
INIT_LIST_HEAD(&sp->so_states); // 初始化nfs4_state结构所在的链表
nfs4_init_seqid_counter(&sp->so_seqid); // 初始化nfs_seqid_counter结构
atomic_set(&sp->so_count, 1); // 初始化引用计数为1
INIT_LIST_HEAD(&sp->so_lru); // 当nfs4_state_owner不再使用时可以挂载到lru链表中.
return sp;
}
nfs4_alloc_state_owner()的流程很简单,就是分配一块内存,然后初始化nfs4_state_owner结构中的各个字段,so_seqid字段的初始化是在函数nfs4_init_seqid_counter()中完成的。
static void
nfs4_init_seqid_counter(struct nfs_seqid_counter *sc)
{
sc->create_time = ktime_get(); // 创建时间
sc->flags = 0;
sc->counter = 0; // 初始化计数为0,这是RPC等待队列中任务(nfs_seqid结构)的数量.
spin_lock_init(&sc->lock); // 初始化自旋锁
INIT_LIST_HEAD(&sc->list); // 初始化链表头为空
// 这个队列中放的是RPC任务.
rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue"); // 初始化RPC等待队列
}
但是nfs4_init_seqid_counter()并没有初始化nfs_seqid_counter结构中的owner_id字段,owner_id是nfs4_state_owner结构的编号,这个编号是依靠idr机制实现的。简单来说,idr就是一种编号分配机制,idr负责给要管理的对象分配一个编号,可以根据这个编号找到被管理的对象。owner_id是在函数nfs4_insert_state_owner_locked()中设置的。
static struct nfs4_state_owner *
nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
{
struct nfs_server *server = new->so_server; // 找到nfs_server结构.
struct rb_node **p = &server->state_owners.rb_node, // 这是红黑树的根节点.
*parent = NULL;
struct nfs4_state_owner *sp;
int err;
while (*p != NULL) {
parent = *p;
sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); // 取出nfs4_state_owner结构.
if (new->so_cred < sp->so_cred)
p = &parent->rb_left; // 向左查找
else if (new->so_cred > sp->so_cred)
p = &parent->rb_right; // 向右查找
else { // 找到符合条件的nfs4_state_owner结构了
if (!list_empty(&sp->so_lru))
list_del_init(&sp->so_lru); // 先从lru链表中删除.
atomic_inc(&sp->so_count); // 增加引用计数
return sp; // 然后返回.
}
}
// 好吧,不存在符合条件的nfs4_state_owner结构,可以使用new了.
// 这里在分配owner_id,每个owner_id代表了一个nfs4_state_owner结构.
err = ida_get_new(&server->openowner_id, &new->so_seqid.owner_id);
if (err)
return ERR_PTR(err);
// 将新分配的nfs4_state_owner结构插入到红黑树中
rb_link_node(&new->so_server_node, parent, p);
rb_insert_color(&new->so_server_node, &server->state_owners);
return new;
}
一般情况下nfs4_state_owner保存在一棵红黑树中,当nfs4_state_owner废弃不用时(比如用户关闭了文件)会链接到一个LRU链表中。nfs4_get_state_owner()会调用nfs4_gc_state_owners()清理LRU链表。如果nfs4_state_owner结构加入到LRU链表中的时间超过了90秒就会被释放。
static void nfs4_gc_state_owners(struct nfs_server *server)
{
struct nfs_client *clp = server->nfs_client; // 找到NFS客户端结构
struct nfs4_state_owner *sp, *tmp;
unsigned long time_min, time_max;
LIST_HEAD(doomed); // 初始化一个临时链表
spin_lock(&clp->cl_lock);
time_max = jiffies; // 现在时刻
time_min = (long)time_max - (long)clp->cl_lease_time; // cl_lease_time = 90秒
// 遍历LRU链表中每一个nfs4_state_owner结构
list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
/* NB: LRU is sorted so that oldest is at the head */
// so_expires表示sp添加到LRU链表中的时间
if (time_in_range(sp->so_expires, time_min, time_max))
break; // 如果添加到LRU链表中的时间不超过90秒,就不处理了.
// 添加到LRU链表中的时间超过了90秒,清理LRU链表.
list_move(&sp->so_lru, &doomed); // 移动到临时链表doomed中.
nfs4_remove_state_owner_locked(sp); // 这个函数从红黑树中删除sp,释放id
}
spin_unlock(&clp->cl_lock);
// 释放临时链表中所有的nfs4_state_owner结构.
list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
list_del(&sp->so_lru);
nfs4_free_state_owner(sp); // 释放nfs4_state_owner结构
}
}
nfs4_state表示一个文件的打开状态,创建完nfs4_state_owner后还需要创建一个nfs4_state结构,这是由函数nfs4_get_open_state实现的,这个函数的流程如下:
参数inode:这是文件索引节点,表示我们要打开的文件
参数owner:这是前面创建的nfs4_state_owner结构,表示客户端一个用户
struct nfs4_state *
nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
{
struct nfs4_state *state, *new;
struct nfs_inode *nfsi = NFS_I(inode); // 取出nfs_inode结构
spin_lock(&inode->i_lock); // 加锁
// 查找符合条件的nfs4_state结构。如果不存在,这个函数会返回NULL.
// 如果存在就增加state的引用计数,因为我们要使用这个数据结构了.
state = __nfs4_find_state_byowner(inode, owner);
spin_unlock(&inode->i_lock); // 解锁
if (state)
goto out; // 找到了
// 没有找到,那么就创建一个新的nfs4_state结构,初始化引用计数为1.
new = nfs4_alloc_open_state();
spin_lock(&owner->so_lock);
spin_lock(&inode->i_lock);
// 由于nfs4_alloc_open_state()分配内存过程中可能休眠,再次查找是否存在符合条件的nfs4_state结构.
state = __nfs4_find_state_byowner(inode, owner);
// 使用新创建的nfs4_state结构.
if (state == NULL && new != NULL) {
state = new; // OK
state->owner = owner; // 所属于的nfs4_state_owner结构
// 因为添加了一个nfs4_state结构,所以需要增加nfs4_state_owner的引用计数.
atomic_inc(&owner->so_count);
list_add(&state->inode_states, &nfsi->open_states); // 挂载到nfs_inode中
ihold(inode);
state->inode = inode; // 设置文件索引节点
spin_unlock(&inode->i_lock);
/* Note: The reclaim code dictates that we add stateless
* and read-only stateids to the end of the list */
// 挂载到nfs4_state_owner结构的链表中
list_add_tail(&state->open_states, &owner->so_states);
spin_unlock(&owner->so_lock);
} else { // state==NULL&&new==NULL state!=NULL&new==NULL state!=NULL&new!=NULL
spin_unlock(&inode->i_lock);
spin_unlock(&owner->so_lock);
if (new)
nfs4_free_open_state(new); // 直接释放内存
}
out:
return state;
}