11

我正在寻找一个明确的规范来描述 ioctl 0x1268 (BLKSSZGET) 的预期参数和行为。

这个数字在很多地方都有声明(其中没有一个包含明确的参考来源),例如linux/fs.h,但我找不到它的规范。

当然,有人在过去的某个时候决定 0x1268 将获得设备的物理扇区大小,并在某处记录了这一点。这些信息来自哪里,我在哪里可以找到它?

编辑:我不是在问 BLKSSZGET 一般做什么,也不是在问它定义在什么标头中。我正在寻找一个明确的标准化源,它说明它应该采用什么参数类型以及它的行为对于任何驱动程序应该是什么实现它。

具体来说,我问是因为在 util-linux 2.23(和 2.24)中似乎存在一个错误,blkdiscard其中扇区大小被查询到 a uint64_t,但是高 32 位没有被触及,因为 BLKSSZGET似乎期望一个 32 位整数,这会导致不正确的扇区大小、不正确的对齐计算以及blkdiscard应该成功的时间失败。因此,在我提交补丁之前,我需要确定问题是否blkdiscard应该使用 32 位整数,或者我的内核中的驱动程序实现是否应该使用 64 位整数。

编辑 2:由于我们正在讨论该主题,因此假设的补丁blkdiscard不正确是:

--- sys-utils/blkdiscard.c-2.23 2013-11-01 18:28:19.270004947 -0400
+++ sys-utils/blkdiscard.c  2013-11-01 18:29:07.334002382 -0400
@@ -71,7 +71,8 @@
 {
    char *path;
    int c, fd, verbose = 0, secure = 0;
-   uint64_t end, blksize, secsize, range[2];
+   uint64_t end, blksize, range[2];
+   uint32_t secsize;
    struct stat sb;

    static const struct option longopts[] = {
@@ -146,8 +147,8 @@
        err(EXIT_FAILURE, _("%s: BLKSSZGET ioctl failed"), path);

    /* align range to the sector size */
-   range[0] = (range[0] + secsize - 1) & ~(secsize - 1);
-   range[1] &= ~(secsize - 1);
+   range[0] = (range[0] + (uint64_t)secsize - 1) & ~((uint64_t)secsize - 1);
+   range[1] &= ~((uint64_t)secsize - 1);

    /* is the range end behind the end of the device ?*/
    end = range[0] + range[1];

适用于例如https://www.kernel.org/pub/linux/utils/util-linux/v2.23/

4

1 回答 1

12

“这是在哪里指定的?”的答案 似乎确实是内核源代码。

我在这里问了内核邮件列表上的问题:https ://lkml.org/lkml/2013/11/1/620

作为回应,Theodore Ts'o写道(注意:他错误地将 sys-utils/blkdiscard.c 标识在他的列表中,但这无关紧要):

BLKSSZGET returns an int.  If you look at the sources of util-linux
v2.23, you'll see it passes an int to BLKSSZGET in 

    sys-utils/blkdiscard.c
    lib/blkdev.c

E2fsprogs also expects BLKSSZGET to return an int, and if you look at
the kernel sources, it very clearly returns an int.

The one place it doesn't is in sys-utils/blkdiscard.c, where as you
have noted, it is passing in a uint64 to BLKSSZGET.  This looks like
it's a bug in sys-util/blkdiscard.c.

然后他继续向 util-linux 的 blkdiscard 提交补丁¹:

--- a/sys-utils/blkdiscard.c
+++ b/sys-utils/blkdiscard.c
@@ -70,8 +70,8 @@ static void __attribute__((__noreturn__)) usage(FILE *out)
 int main(int argc, char **argv)
 {
        char *path;
-       int c, fd, verbose = 0, secure = 0;
-       uint64_t end, blksize, secsize, range[2];
+       int c, fd, verbose = 0, secure = 0, secsize;
+       uint64_t end, blksize, range[2];
        struct stat sb;

        static const struct option longopts[] = {

我一直犹豫在我的邮件列表帖子和这个 SO 问题的原始版本中提到 blkdiscard 工具,特别是因为这个原因:我知道我的内核源代码中有什么,修改 blkdiscard 以同意源代码已经很容易了,并且这最终分散了“这在哪里记录?”的真正问题。

所以,至于细节,比我更官方的人也表示 BLKSSZGET ioctl 需要一个 int,但关于文档的一般问题仍然存在。然后我跟进了https://lkml.org/lkml/2013/11/3/125并收到了 Theodore Ts'o 的另一个回复(维基可信度)回答了这个问题。他写道

> There was a bigger question hidden behind the context there that I'm
> still wondering about: Are these ioctl interfaces specified and
> documented somewhere? From what I've seen, and from your response, the
> implication is that the kernel source *is* the specification, and not
> document exists that the kernel is expected to comply with; is this
> the case?

The kernel source is the specification.  Some of these ioctl are
documented as part of the linux man pages, for which the project home
page is here:

     https://www.kernel.org/doc/man-pages/

However, these document existing practice; if there is a discrepancy
between what is in the kernel has implemented and the Linux man pages,
it is the Linux man pages which are buggy and which will be changed.
That is man pages are descriptive, not perscriptive.

我还询问了在公共内核 API 中一般使用“int”的情况,他的回答就在那里,尽管这在这里是题外话。

答:所以,你有它,最后的答案是:ioctl 接口是由内核源本身指定的;没有内核遵守的文档。有文档描述内核对各种 ioctl 的实现,但如果有不匹配的地方,那是文档中的错误,而不是内核中的错误。

¹ 考虑到以上所有,我想指出,与我的相比,Theodore Ts'o 提交的补丁的一个重要区别是使用“int”而不是“uint32_t”——BLKSSZGET,根据内核源,确实期望一个参数是平台上任何大小的“int”,而不是强制的 32 位值。

于 2013-11-03T17:56:56.507 回答