Coda Kernel-Venus 接口

注意

这是描述 Coda 组件的技术文档之一 - 本文档描述了客户端内核-Venus 接口。

更多信息

运行 Coda 所需的用户级软件

要运行 Coda,您需要获取客户端的用户级缓存管理器 Venus,以及操作 ACL、登录等的工具。客户端需要在内核配置中选择 Coda 文件系统。

服务器需要用户级服务器,目前不依赖于内核支持。

Venus 内核接口

Peter J. Braam

v1.0, 1997 年 11 月 9 日

本文档描述了 Venus 和 Coda 文件系统操作所需的内核级文件系统代码之间的通信。 本文档旨在描述当前接口(版本 1.0)以及我们设想的改进。

1. 简介

Coda 分布式文件系统中的一个关键组件是缓存管理器 Venus。

当启用了 Coda 的系统上的进程访问 Coda 文件系统中的文件时,请求将定向到操作系统中的文件系统层。操作系统将与 Venus 通信以服务于进程的请求。 Venus 管理持久性客户端缓存,并向 Coda 文件服务器和相关服务器(例如身份验证服务器)发出远程过程调用,以服务于从操作系统收到的这些请求。当 Venus 服务完请求后,它会将适当的返回代码以及与请求相关的其他数据回复给操作系统。 可选地,Coda 的内核支持可以维护最近处理的请求的微型缓存,以限制与 Venus 的交互次数。 当微型缓存中的元素不再有效时,Venus 具有通知内核的功能。

本文档精确地描述了内核和 Venus 之间的这种通信。 将给出所谓的向上调用和向下调用的定义,并带有它们处理的数据格式。 我们还将描述由调用产生的语义不变式。

历史上,Coda 是在 Mach 2.6 中的 BSD 文件系统中实现的。内核和 Venus 之间的接口与 BSD VFS 接口非常相似。 提供了类似的功能,并且参数和返回数据的格式与 BSD VFS 非常相似。 这为在 BSD 系统中实现 Coda 的内核级文件系统驱动程序提供了一个几乎自然的环境。 但是,其他操作系统(如 Linux 和 Windows 95 和 NT)具有具有不同接口的虚拟文件系统。

要在这些系统上实现 Coda,需要对 Venus/Kernel 协议进行一些逆向工程。 此外,人们发现其他系统可以从该协议的某些小型优化和修改中获益匪浅。 为了促进这项工作并使未来的移植更加容易,应该非常详细地记录 Venus 和内核之间的通信。 这是本文档的目标。

2. 服务 Coda 文件系统调用

对 Coda 文件系统服务的请求服务源于访问 Coda 文件的进程 P。 它发出一个系统调用,该调用会陷入到 OS 内核。 陷入内核的此类调用的示例是 Unix 上下文中的 readwriteopenclosecreatemkdirrmdirchmod。 Win32 环境中存在类似的调用,并命名为 CreateFile

通常,操作系统在虚拟文件系统 (VFS) 层中处理请求,在 NT 中命名为 I/O 管理器,在 Windows 95 中命名为 IFS 管理器。 VFS 负责对请求进行部分处理,并负责定位将服务于请求部分的特定文件系统。 通常,路径中的信息有助于定位正确的 FS 驱动程序。 有时,在经过广泛的预处理后,VFS 开始调用 FS 驱动程序中导出的例程。 这是 FS 特定请求处理开始的点,并且 Coda 特定内核代码在此处发挥作用。

Coda 的 FS 层必须公开和实现多个接口。 首先,VFS 必须能够对 Coda FS 层进行所有必要的调用,因此 Coda FS 驱动程序必须公开 VFS 接口,如适用于操作系统。 这些操作系统之间的差异非常大,但共享诸如读取/写入、创建和删除对象等功能。 Coda FS 层通过调用缓存管理器 Venus 提供的一个或多个明确定义的服务来服务此类 VFS 请求。 当来自 Venus 的回复返回到 FS 驱动程序时,VFS 调用的服务将继续,并以回复内核的 VFS 结束。 最后,VFS 层返回到进程。

因此,FS 驱动程序公开的基本接口必须允许 Venus 管理消息流量。 特别是,Venus 必须能够检索和放置消息,并被通知新消息的到达。 通知必须通过一种不会阻塞 Venus 的机制,因为即使没有消息正在等待或处理,Venus 也必须处理其他任务。

Coda FS 驱动程序的接口

此外,FS 层还提供用户进程和 Venus 之间的一种特殊通信路径,称为 pioctl 接口。 pioctl 接口用于 Coda 特定服务,例如请求有关 Venus 管理的持久缓存的详细信息。 在这里,内核的参与度很小。 它识别调用进程并将信息传递给 Venus。 当 Venus 回复时,响应将以未修改的形式传递回调用方。

最后,Venus 允许内核 FS 驱动程序缓存来自某些服务的结果。 这样做是为了避免过多的上下文切换并产生一个高效的系统。 但是,Venus 可能会获取信息,例如来自网络的信息,这意味着必须刷新或替换缓存的信息。 然后,Venus 向 Coda FS 层发出向下调用,以请求刷新或更新缓存。 内核 FS 驱动程序同步处理此类请求。

在这些接口中,VFS 接口以及放置、接收和通知消息的功能是特定于平台的。 我们不会深入研究导出到 VFS 层的调用,但我们会说明消息交换机制的要求。

3. 消息层

在最低级别,Venus 和 FS 驱动程序之间的通信通过消息进行。 请求 Coda 文件服务的进程和 Venus 之间的同步依赖于阻塞和唤醒进程。 Coda FS 驱动程序代表进程 P 处理 VFS 和 pioctl 请求,为 Venus 创建消息,等待回复,最后返回到调用方。 消息交换的实现是特定于平台的,但是语义(到目前为止)似乎普遍适用。 数据缓冲区由 FS 驱动程序代表 P 在内核内存中创建,并复制到 Venus 中的用户内存。

FS 驱动程序在服务 P 时向上调用 Venus。 通过创建消息结构将此类向上调用分派到 Venus。 该结构包含 P 的标识、消息序列号、请求的大小以及指向内核内存中请求数据的指针。 由于数据缓冲区被重用来保存来自 Venus 的回复,因此有一个用于回复大小的字段。 消息中的标志字段用于精确记录消息的状态。 其他平台相关的结构包括指向确定消息在队列上的位置的指针和指向同步对象的指针。 在向上调用例程中,填写消息结构,将标志设置为 0,并将其放置在 _pending_ 队列中。 调用向上调用例程负责分配数据缓冲区; 其结构将在下一节中描述。

必须存在一种通知 Venus 消息已被创建的工具,并使用 OS 中可用的同步对象来实现。 此通知在进程 P 的向上调用上下文中完成。 当消息位于挂起队列中时,进程 P 无法在向上调用中继续。 必须暂停文件系统请求例程中 P 的(内核模式)处理,直到 Venus 回复。 因此,P 中的调用线程在向上调用中被阻塞。 消息结构中的指针将定位 P 正在休眠的同步对象。

Venus 检测到消息已到达的通知,FS 驱动程序允许 Venus 使用 getmsg_from_kernel 调用来检索消息。 此操作在内核中通过将消息放置在正在处理的消息队列上并将标志设置为 READ 来完成。 Venus 传递数据缓冲区的内容。 现在 getmsg_from_kernel 调用返回,Venus 处理该请求。

在稍后的某个时刻,FS 驱动程序收到来自 Venus 的消息,即当 Venus 调用 sendmsg_to_kernel 时。 此时,Coda FS 驱动程序会查看消息的内容并确定

  • 该消息是否是针对暂停线程 P 的回复。 如果是这样,它将从处理队列中删除该消息,并将该消息标记为 WRITTEN。 最后,FS 驱动程序取消阻塞 P(仍在 Venus 的内核模式上下文中),并且 sendmsg_to_kernel 调用返回到 Venus。 进程 P 将在某个时候被调度,并继续处理其向上调用,并将数据缓冲区替换为来自 Venus 的回复。

  • 该消息是一个 downcall。 向下调用是从 Venus 到 FS 驱动程序的请求。 FS 驱动程序立即处理该请求(通常是缓存驱逐或替换),并且当它完成时,sendmsg_to_kernel 返回。

现在 P 醒来并继续处理向上调用。 有一些微妙之处需要考虑。 首先,P 将确定它是否由于来自其他来源的信号(例如尝试终止 P)而在向上调用中被唤醒,或者像通常那样被 Venus 在其 sendmsg_to_kernel 调用中被唤醒。 在正常情况下,向上调用例程将释放消息结构并返回。 FS 例程可以继续进行其处理。

睡眠和 IPC 安排

如果 P 因信号而不是 Venus 唤醒,它将首先查看标志字段。 如果消息尚未 READ,则进程 P 可以处理其信号而无需通知 Venus。 如果 Venus 已 READ,并且不应处理该请求,则 P 可以向 Venus 发送信号消息,以指示它应忽略先前的消息。 此类信号放置在队列的头部,并由 Venus 首先读取。 如果消息已被标记为 WRITTEN,则停止处理为时已晚。 VFS 例程现在将继续。 (-- 如果 VFS 请求涉及多个向上调用,则这可能会导致复杂的状态,可以在消息结构中添加一个额外的字段“handle_signals”,以指示已通过的不可返回点。--)

3.1. 实现细节

此机制的 Unix 实现已通过与 Coda 关联的字符设备的实现。 Venus 通过对设备执行读取来检索消息,回复通过写入发送,通知通过设备的文件描述符上的 select 系统调用。 进程 P 在可中断的等待队列对象上保持等待状态。

在 Windows NT 和 DPMI Windows 95 实现中,使用了 DeviceIoControl 调用。 DeviceIoControl 调用旨在通过 OPCODES 将缓冲区从用户内存复制到内核内存。 sendmsg_to_kernel 作为同步调用发出,而 getmsg_from_kernel 调用是异步的。 Windows EventObjects 用于通知消息的到达。 进程 P 在 NT 中的 KernelEvent 对象和 Windows 95 中的信号量上保持等待状态。

4. 调用级别的接口

本节描述了 Coda FS 驱动程序可以对 Venus 进行的向上调用。 每个向上调用都使用两个结构:inputArgs 和 outputArgs。 以伪 BNF 形式,这些结构采用以下形式

struct inputArgs {
    u_long opcode;
    u_long unique;     /* Keep multiple outstanding msgs distinct */
    u_short pid;                 /* Common to all */
    u_short pgid;                /* Common to all */
    struct CodaCred cred;        /* Common to all */

    <union "in" of call dependent parts of inputArgs>
};

struct outputArgs {
    u_long opcode;
    u_long unique;       /* Keep multiple outstanding msgs distinct */
    u_long result;

    <union "out" of call dependent parts of inputArgs>
};

在继续之前,让我们阐明各个字段的作用。 inputArgs 以 opcode 开头,该 opcode 定义了从 Venus 请求的服务类型。 目前大约有 30 个向上调用,我们将讨论它们。 unique 字段使用唯一编号标记 inputArg,该编号将唯一地标识该消息。 传递进程 ID 和进程组 ID。 最后,包括调用者的凭据。

在深入研究特定调用之前,我们需要讨论内核和 Venus 共享的各种数据结构。

4.1. 内核和 Venus 共享的数据结构

CodaCred 结构定义了各种用户 ID 和组 ID,因为它们是为调用进程设置的。 vuid_t 和 vgid_t 是 32 位无符号整数。 它还在数组中定义了组成员身份。 在 Unix 上,CodaCred 已被证明足以实现 Coda 的良好安全语义,但是当这些成熟时,该结构可能必须针对 Windows 环境进行修改

struct CodaCred {
    vuid_t cr_uid, cr_euid, cr_suid, cr_fsuid; /* Real, effective, set, fs uid */
    vgid_t cr_gid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */
    vgid_t cr_groups[NGROUPS];        /* Group membership for caller */
};

注意

我们是否需要在 Venus 中使用 CodaCreds 值得怀疑。 最后,Venus 不知道组,尽管它使用默认的 uid/gid 创建文件。 也许组成员身份列表是多余的。

下一项是用于标识 Coda 文件的基本标识符,即 ViceFid。 文件的 Fid 唯一地定义了 Coda 文件系统中一个单元中的文件或目录 [1]

typedef struct ViceFid {
    VolumeId Volume;
    VnodeId Vnode;
    Unique_t Unique;
} ViceFid;

每个组成字段:VolumeId、VnodeId 和 Unique_t 都是无符号的 32 位整数。 我们设想还需要添加一个进一步的字段,以标识 Coda 单元; 这可能会采用 Ipv6 大小的 IP 地址的形式,通过 DNS 命名 Coda 单元。

Venus 和内核之间共享的下一个重要结构是文件的属性。 以下结构用于交换信息。 它有空间用于将来的扩展,例如对设备文件的支持(当前在 Coda 中不存在)

struct coda_timespec {
        int64_t         tv_sec;         /* seconds */
        long            tv_nsec;        /* nanoseconds */
};

struct coda_vattr {
        enum coda_vtype va_type;        /* vnode type (for create) */
        u_short         va_mode;        /* files access mode and type */
        short           va_nlink;       /* number of references to file */
        vuid_t          va_uid;         /* owner user id */
        vgid_t          va_gid;         /* owner group id */
        long            va_fsid;        /* file system id (dev for now) */
        long            va_fileid;      /* file id */
        u_quad_t        va_size;        /* file size in bytes */
        long            va_blocksize;   /* blocksize preferred for i/o */
        struct coda_timespec va_atime;  /* time of last access */
        struct coda_timespec va_mtime;  /* time of last modification */
        struct coda_timespec va_ctime;  /* time file changed */
        u_long          va_gen;         /* generation number of file */
        u_long          va_flags;       /* flags defined for file */
        dev_t           va_rdev;        /* device special file represents */
        u_quad_t        va_bytes;       /* bytes of disk space held by file */
        u_quad_t        va_filerev;     /* file modification number */
        u_int           va_vaflags;     /* operations flags, see below */
        long            va_spare;       /* remain quad aligned */
};

4.2. pioctl 接口

可以通过应用程序通过 pioctl 接口发出 Coda 特定请求。 pioctl 实现为虚构文件 /coda/.CONTROL 上的普通 ioctl。 pioctl 调用打开此文件,获取文件句柄并发出 ioctl 调用。 最后,它关闭该文件。

内核对此的参与仅限于提供打开和关闭的功能,并传递 ioctl 消息,并验证 pioctl 数据缓冲区中的路径是否为 Coda 文件系统中的文件。

内核被传递了一个数据包,其形式为

struct {
    const char *path;
    struct ViceIoctl vidata;
    int follow;
} data;

其中

struct ViceIoctl {
        caddr_t in, out;        /* Data to be transferred in, or out */
        short in_size;          /* Size of input buffer <= 2K */
        short out_size;         /* Maximum size of output buffer, <= 2K */
};

该路径必须是 Coda 文件,否则将不会发出 ioctl 向上调用。

注意

数据结构和代码一团糟。 我们需要清理一下。

我们现在继续记录各个调用:

4.3. root

参数

输入

输出

struct cfs_root_out {
    ViceFid VFid;
} cfs_root;
描述

在 Coda 文件系统初始化期间,会对 Venus 进行此调用。 如果结果为零,则 cfs_root 结构包含 Coda 文件系统的根的 ViceFid。 如果生成非零结果,则其值是平台相关的错误代码,指示 Venus 在定位 Coda 文件系统的根时遇到的困难。

4.4. lookup

总结

如果目录中存在某个对象,则查找该对象的 ViceFid 和类型。

参数

输入

struct  cfs_lookup_in {
    ViceFid     VFid;
    char        *name;          /* Place holder for data. */
} cfs_lookup;

输出

struct cfs_lookup_out {
    ViceFid VFid;
    int vtype;
} cfs_lookup;
描述

进行此调用是为了确定目录条目的 ViceFid 和文件类型。 请求的目录条目带有名称“name”,Venus 将搜索由 cfs_lookup_in.VFid 标识的目录。 结果可能表明该名称不存在,或者在查找该名称时遇到了困难(例如,由于断开连接)。 如果结果为零,则字段 cfs_lookup_out.VFid 包含目标 ViceFid,并且 cfs_lookup_out.vtype 包含 coda_vtype,该 coda_vtype 给出了名称指定的对象类型。

该对象的名称是最大长度为 CFS_MAXNAMLEN 的 8 位字符串,当前设置为 256(包括 0 终止符。)

重要的是要意识到 Venus 会按位或字段 cfs_lookup.vtype 与 CFS_NOCACHE,以指示不应将该对象放置在内核名称缓存中。

注意

vtype 的类型当前错误。 它应该是 coda_vtype。 Linux 不注意 CFS_NOCACHE。 它应该。

4.5. getattr

总结 获取文件的属性。

参数

输入

struct cfs_getattr_in {
    ViceFid VFid;
    struct coda_vattr attr; /* XXXXX */
} cfs_getattr;

输出

struct cfs_getattr_out {
    struct coda_vattr attr;
} cfs_getattr;
描述

此调用返回由 fid 标识的文件的属性。

错误

如果 fid 的对象不存在、无法访问或者调用者无权获取属性,则会发生错误。

注意

许多内核 FS 驱动程序(Linux、NT 和 Windows 95)都需要获取属性以及 Fid,以实例化内部“inode”或“FileHandle”。 通过在 Venus/内核交互级别和 RPC 级别上都组合查找和 getattr 调用,可以在此类系统上实现性能的显着提高。

输入参数中包含的 vattr 结构是多余的,应将其删除。

4.6. setattr

总结

设置文件的属性。

参数

输入

struct cfs_setattr_in {
    ViceFid VFid;
    struct coda_vattr attr;
} cfs_setattr;

输出

描述

结构 attr 填充了要以 BSD 样式更改的属性。 未更改的属性设置为 -1,除了 vtype 设置为 VNON。 其他设置为要分配的值。 FS 驱动程序可能请求更改的唯一属性是模式、所有者、groupid、atime、mtime 和 ctime。 返回值指示成功或失败。

错误

可能会发生各种错误。 对象可能不存在、可能无法访问,或者 Venus 可能未授予权限。

4.7. access

参数

输入

struct cfs_access_in {
    ViceFid     VFid;
    int flags;
} cfs_access;

输出

描述

验证是否允许 VFid 标识的对象执行由标志描述的操作。 结果表明是否将授予访问权限。 重要的是要记住,Coda 使用 ACL 来强制实施保护,并且最终服务器而不是客户端强制实施系统的安全性。 此调用的结果将取决于用户是否持有令牌。

错误

对象可能不存在,或者描述保护的 ACL 可能无法访问。

4.8. create

总结

调用以创建文件

参数

输入

struct cfs_create_in {
    ViceFid VFid;
    struct coda_vattr attr;
    int excl;
    int mode;
    char        *name;          /* Place holder for data. */
} cfs_create;

输出

struct cfs_create_out {
    ViceFid VFid;
    struct coda_vattr attr;
} cfs_create;
描述

调用此向上调用以请求创建文件。 该文件将在由 VFid 标识的目录中创建,其名称将为 name,模式将为 mode。 如果设置了 excl,则如果该文件已存在,将返回一个错误。 如果 attr 中的 size 字段设置为零,则该文件将被截断。 文件的 uid 和 gid 通过使用宏 CRTOUID(此宏与平台相关)将 CodaCred 转换为 uid 来设置。 成功后,将返回该文件的 VFid 和属性。 Coda FS 驱动程序通常会在内核级别为新对象实例化 vnode、inode 或文件句柄。

错误

可能会发生各种错误。 权限可能不足。 如果该对象存在并且不是文件,则在 Unix 下返回错误 EISDIR。

注意

参数的打包效率非常低,并且似乎表明系统调用 creat 和 VFS 操作 create 之间存在混淆。 仅调用 VFS 操作 create 来创建新对象。 此创建调用与 Unix 中的创建调用不同,因为它不会被调用来返回文件描述符。 截断和独占选项以及模式可以简单地成为模式的一部分,就像在 Unix 下一样。 不应有标志参数; 这在 open (2) 中用于返回 READ 或 WRITE 模式的文件描述符。

目录的属性也应该返回,因为大小和 mtime 发生了变化。

4.9. mkdir

总结

创建一个新目录。

参数

输入

struct cfs_mkdir_in {
    ViceFid     VFid;
    struct coda_vattr attr;
    char        *name;          /* Place holder for data. */
} cfs_mkdir;

输出

struct cfs_mkdir_out {
    ViceFid VFid;
    struct coda_vattr attr;
} cfs_mkdir;
描述

此调用类似于 create,但会创建一个目录。 只有输入参数中的 mode 字段用于创建。 成功创建后,返回的 attr 包含新目录的属性。

错误

与 create 相同。

注意

输入参数应更改为模式,而不是属性。

应该返回父目录的属性,因为大小和 mtime 发生了变化。

4.12. remove

总结

删除一个文件

参数

输入

struct cfs_remove_in {
    ViceFid     VFid;
    char        *name;          /* Place holder for data. */
} cfs_remove;

输出

描述

删除由 VFid 标识的目录中名为 cfs_remove_in.name 的文件。

注意

应该返回目录的属性,因为其 mtime 和大小可能会发生变化。

4.13. rmdir

总结

删除一个目录

参数

输入

struct cfs_rmdir_in {
    ViceFid     VFid;
    char        *name;          /* Place holder for data. */
} cfs_rmdir;

输出

描述

从 VFid 标识的目录中删除名称为“name”的目录。

注意

应该返回父目录的属性,因为其 mtime 和大小可能会发生变化。

4.15. open

总结

打开一个文件。

参数

输入

struct cfs_open_in {
    ViceFid     VFid;
    int flags;
} cfs_open;

输出

struct cfs_open_out {
    dev_t       dev;
    ino_t       inode;
} cfs_open;
描述

此请求要求 Venus 将由 VFid 标识的文件放置在其缓存中,并注意到调用进程希望使用 open(2) 中的标志将其打开。 返回到内核的返回值对于 Unix 和 Windows 系统有所不同。 对于 Unix 系统,将通知 Coda FS 驱动程序容器文件的设备和 inode 编号,这些编号在字段 dev 和 inode 中。 对于 Windows,容器文件的路径将返回到内核。

注意

当前,cfs_open_out 结构未正确适应处理 Windows 情况。 最好实现两个向上调用,一个向上调用旨在针对容器文件名打开,另一个向上调用针对容器文件 inode 打开。

4.16. close

总结

关闭一个文件,并在服务器上更新它。

参数

输入

struct cfs_close_in {
    ViceFid     VFid;
    int flags;
} cfs_close;

输出

描述

关闭由 VFid 标识的文件。

注意

flags 参数是虚假的,未使用。 但是,Venus 的代码有空间来处理 execp 输入字段,可能该字段应用于通知 Venus 文件已关闭但仍映射到内存以进行执行。 有关在 Venus vproc_vfscalls 中获取与不获取数据的注释。 这似乎很傻。 如果要关闭文件,则容器文件中的数据将成为新数据。 同样,execp 标志可能会起作用以造成混淆:当前,Venus 可能会认为文件仍然映射到内存时可以从缓存中刷新该文件。 这需要理解。

4.17. ioctl

总结

在文件上执行 ioctl。 这包括 pioctl 接口。

参数

输入

struct cfs_ioctl_in {
    ViceFid VFid;
    int cmd;
    int len;
    int rwflag;
    char *data;                 /* Place holder for data. */
} cfs_ioctl;

输出

struct cfs_ioctl_out {
    int len;
    caddr_t     data;           /* Place holder for data. */
} cfs_ioctl;
描述

在文件上执行 ioctl 操作。 command、len 和 data 参数按 usual 填充。 Venus 不使用标志。

注意

另一个虚假参数。 不使用标志。 Venus 代码中的 PREFETCHING 业务是什么?

4.18. rename

总结

重命名一个 fid。

参数

输入

struct cfs_rename_in {
    ViceFid     sourceFid;
    char        *srcname;
    ViceFid destFid;
    char        *destname;
} cfs_rename;

输出

描述

将目录 sourceFid 中名称为 srcname 的对象重命名为 destFid 中的 destname。 重要的是名称 srcname 和 destname 是以 0 结尾的字符串。 Unix 内核中的字符串并不总是以 null 结尾的。

4.19. readdir

总结

读取目录条目。

参数

输入

struct cfs_readdir_in {
    ViceFid     VFid;
    int count;
    int offset;
} cfs_readdir;

输出

struct cfs_readdir_out {
    int size;
    caddr_t     data;           /* Place holder for data. */
} cfs_readdir;
描述

从 VFid 读取目录条目,从偏移量 offset 开始,最多读取 count 个字节。 在数据中返回数据,并在大小中返回大小。

注意

不使用此调用。 Readdir 操作利用容器文件。 在即将进行的目录改版期间,我们将重新评估此问题。

4.20. vget

总结

指示 Venus 执行 FSDB->Get。

参数

输入

struct cfs_vget_in {
    ViceFid VFid;
} cfs_vget;

输出

struct cfs_vget_out {
    ViceFid VFid;
    int vtype;
} cfs_vget;
描述

此向上调用要求 Venus 对 VFid 标记的 fsobj 执行 get 操作。

注意

不使用此操作。 但是,它非常有用,因为它可以用于处理读/写内存映射文件。 可以使用 vget 将它们“固定”在 Venus 缓存中,并使用 inactive 释放它们。

4.21. fsync

总结

告诉 Venus 更新文件的 RVM 属性。

参数

输入

struct cfs_fsync_in {
    ViceFid VFid;
} cfs_fsync;

输出

描述

要求 Venus 更新对象 VFid 的 RVM 属性。 这应该作为内核级别 fsync 类型调用的一部分来调用。 结果表明同步是否成功。

注意

Linux 不实现此调用。 它应该。

4.22. inactive

总结

告诉 Venus vnode 不再使用。

参数

输入

struct cfs_inactive_in {
    ViceFid VFid;
} cfs_inactive;

输出

描述

此操作返回 EOPNOTSUPP。

注意

这也许应该删除。

4.23. rdwr

总结

从文件读取或写入

参数

输入

struct cfs_rdwr_in {
    ViceFid     VFid;
    int rwflag;
    int count;
    int offset;
    int ioflag;
    caddr_t     data;           /* Place holder for data. */
} cfs_rdwr;

输出

struct cfs_rdwr_out {
    int rwflag;
    int count;
    caddr_t     data;   /* Place holder for data. */
} cfs_rdwr;
描述

此向上调用要求 Venus 从文件读取或写入。

注意

应该删除它,因为它与 Coda 的理念背道而驰,即读取/写入操作永远不会到达 Venus。 有人告诉我该操作不起作用。 当前未使用。

4.24. odymount

总结

允许在一个 Unix 挂载点上挂载多个 Coda “文件系统”。

参数

输入

struct ody_mount_in {
    char        *name;          /* Place holder for data. */
} ody_mount;

输出

struct ody_mount_out {
    ViceFid VFid;
} ody_mount;
描述

要求 Venus 返回名为 name 的 Coda 系统的 rootfid。 该 fid 在 VFid 中返回。

注意

David 曾使用此调用来进行动态集操作。它应该被移除,因为它会在 VFS 挂载区域造成指针的混乱。Coda 本身并没有使用它。Venus 没有实现此调用。

4.25. ody_lookup

总结

查找某些东西。

参数

输入

不相关

输出

不相关

注意

删除它。Venus 没有实现此调用。

4.26. ody_expand

总结

在动态集中扩展某些东西。

参数

输入

不相关

输出

不相关

注意

删除它。Venus 没有实现此调用。

4.27. prefetch

总结

预取一个动态集。

参数

输入

没有文档。

输出

没有文档。

描述

Venus 的 worker.cc 拥有对此调用的支持,尽管已经注明它不能工作。这并不奇怪,因为内核没有对此调用提供支持。(ODY_PREFETCH 未被定义的操作)。

注意

删除它。它不能工作,并且 Coda 没有使用它。

4.28. signal

总结

向 Venus 发送一个关于 upcall 的信号。

参数

输入

输出

不适用。

描述

这是一个带外的 upcall,用于通知 Venus:调用进程在 Venus 从输入队列读取消息后接收到一个信号。Venus 应该清理该操作。

错误

没有给出回复。

注意

我们需要更好地理解 Venus 需要清理什么,以及它是否正确地执行了清理。此外,我们需要正确地处理每个系统调用多次 upcall 的情况。重要的是要知道在内核负责通知 Venus 清理的 upcall 之后,Venus 发生了哪些状态变化(例如,open 肯定是一个这样的状态变化,但许多其他的可能不是)。

5. 迷你缓存和 downcall

Coda FS 驱动程序可以缓存查找和访问 upcall 的结果,以限制 upcall 的频率。Upcall 带来成本,因为需要进行进程上下文切换。缓存信息的对应物是 Venus 将通知 FS 驱动程序,缓存的条目必须被刷新或重命名。

内核代码通常需要维护一个结构,将内部文件句柄(在 BSD 中称为 vnode,在 Linux 中称为 inode,在 Windows 中称为 FileHandle)与 Venus 维护的 ViceFid 链接起来。原因是需要频繁地进行来回转换,以便发出 upcall 并使用 upcall 的结果。这样的链接对象被称为 cnode。

当前迷你缓存的实现具有记录以下内容的缓存条目:

  1. 文件的名称

  2. 包含对象的目录的 cnode

  3. 允许查找的 CodaCred 的列表。

  4. 对象的 cnode

Coda FS 驱动程序可以通过传递对象的名称、目录和调用者的 CodaCred,从缓存中请求所需对象的 cnode。缓存将返回 cnode 或指示无法找到它。当 Coda FS 驱动程序修改或删除对象时,必须小心地使缓存条目失效。

当 Venus 获得表明缓存条目不再有效的信息时,它将对内核进行 downcall。Downcall 被 Coda FS 驱动程序拦截,并导致以下描述的缓存失效。除非 downcall 数据无法读取到内核内存中,否则 Coda FS 驱动程序不会返回错误。

5.1. INVALIDATE

没有关于此调用的信息可用。

5.2. FLUSH

参数

总结

完全刷新名称缓存。

描述

Venus 在启动和死亡时发出此调用。这是为了防止持有过时的缓存信息。某些操作系统允许动态关闭内核名称缓存。当这样做时,会发出此 downcall。

5.3. PURGEUSER

参数
struct cfs_purgeuser_out {/* CFS_PURGEUSER is a venus->kernel call */
    struct CodaCred cred;
} cfs_purgeuser;
描述

删除缓存中携带 Cred 的所有条目。当用户的令牌过期或被刷新时,会发出此调用。

5.4. ZAPFILE

参数
struct cfs_zapfile_out {  /* CFS_ZAPFILE is a venus->kernel call */
    ViceFid CodaFid;
} cfs_zapfile;
描述

删除具有 (dir vnode, name) 对的所有条目。 这是因为 vnode 的缓存属性失效而发出的。

注意

在 NetBSD 和 Mach 中,此调用的名称不正确。迷你缓存 zapfile 例程采用不同的参数。Linux 没有正确实现属性的失效。

5.5. ZAPDIR

参数
struct cfs_zapdir_out {   /* CFS_ZAPDIR is a venus->kernel call */
    ViceFid CodaFid;
} cfs_zapdir;
描述

删除缓存中位于目录 CodaFid 中的所有条目,以及该目录的所有子项。当 Venus 接收到目录的回调时,会发出此调用。

5.6. ZAPVNODE

参数
struct cfs_zapvnode_out { /* CFS_ZAPVNODE is a venus->kernel call */
    struct CodaCred cred;
    ViceFid VFid;
} cfs_zapvnode;
描述

删除缓存中携带 cred 和 VFid 的所有条目,如参数所示。这个 downcall 可能永远不会发出。

5.7. PURGEFID

参数
struct cfs_purgefid_out { /* CFS_PURGEFID is a venus->kernel call */
    ViceFid CodaFid;
} cfs_purgefid;
描述

刷新文件的属性。如果它是一个目录(奇数 vnode),则从 namecache 中清除其子项,并从 namecache 中删除该文件。

5.8. REPLACE

总结

替换名称集合的 Fid。

参数
struct cfs_replace_out { /* cfs_replace is a venus->kernel call */
    ViceFid NewFid;
    ViceFid OldFid;
} cfs_replace;
描述

此例程用另一个替换名称缓存中的 ViceFid。添加它是为了允许 Venus 在重新集成期间,即使这些 fid 上的引用计数不为零,也能用全局 fid 替换本地分配的临时 fid (当与网络断开时)。

6. 初始化和清理

本节简要提示 Coda FS 驱动程序在启动和关闭时或 Venus 发生故障时需要的功能。在进入讨论之前,有必要重复一下 Coda FS 驱动程序维护以下数据

  1. 消息队列

  2. cnodes

  3. 名称缓存条目

    名称缓存条目完全是驱动程序私有的,因此可以很容易地操作它们。消息队列通常具有清晰的初始化和销毁点。 cnodes 更加微妙。用户进程在 Coda 文件系统中持有引用计数,因此很难清理 cnodes。

它可以期望通过以下方式发出请求

  1. 消息子系统

  2. VFS 层

  3. pioctl 接口

    目前 pioctl 通过 Coda 的 VFS 传递,因此我们可以类似地对待它们。

6.1. 要求

应满足以下要求

  1. 消息队列应具有打开和关闭例程。在 Unix 上,打开字符设备就是这样的例程。

  • 在打开之前,不能放置任何消息。

  • 打开将删除任何仍在挂起的旧消息。

  • 关闭将通知任何睡眠进程,它们的 upcall 无法完成。

  • 关闭将释放消息队列分配的所有内存。

  1. 在打开时,名称缓存应初始化为空状态。

  2. 在消息队列打开之前,所有 VFS 操作都将失败。幸运的是,这可以通过确保在打开之前无法成功挂载 Coda 文件系统来实现。

  3. 关闭队列后,不能成功进行任何 VFS 操作。这里需要小心,因为一些操作(lookup, read/write, readdir)可以在没有 upcall 的情况下进行。必须显式阻止这些操作。

  4. 关闭时,应刷新并禁用名称缓存。

  5. 可以释放 cnode 持有的所有内存,而无需依赖 upcall。

  6. 卸载文件系统可以在不依赖 upcall 的情况下完成。

  7. 如果 Venus 无法获得 rootfid 或 rootfid 的属性,则挂载 Coda 文件系统应优雅地失败。最好的实现方法是 Venus 在尝试挂载之前获取这些对象。

注意

特别是 NetBSD,还有 Linux,尚未完全实现上述要求。为了平稳运行,需要对此进行纠正。