ioctl 参数(例如 0x1268 / BLKSSZGET)实际指定在哪里?

Posted

技术标签:

【中文标题】ioctl 参数(例如 0x1268 / BLKSSZGET)实际指定在哪里?【英文标题】:Where are ioctl parameters (such as 0x1268 / BLKSSZGET) actually specified? 【发布时间】:2013-11-01 22:12:18 【问题描述】:

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

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

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

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

具体来说,我之所以问是因为 似乎 在 util-linux 2.23(和 2.24)中的blkdiscard 中存在一个错误,其中扇区大小被查询到uint64_t,但是由于 BLKSSZGET 似乎需要一个 32 位整数,因此高 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/.

【问题讨论】:

谢谢,但我已经知道 BLKSSZGET 做了什么。我正在寻找其行为的明确规范。特别是它是否需要一个指向 32 位或 64 位整数的指针作为参数,更重要的是,你怎么知道 很公平,我对此的了解为 0,认为这可能会有所帮助;会继续挖掘。 鉴于可能很少有人知道这一点,您能否详细说明您的 Q 并添加一些上下文? @slm 我刚刚更新了。 好吧,根据block/ioctl.c,它似乎将int 用于BLKSSZGET。不管它是什么意思。它来自bdev_logical_block_size,在include/linux/blkdev.h中定义为static inline unsigned short bdev_logical_block_size(struct block_device *bdev) 【参考方案1】:

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

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

作为回应,Theodore Ts'o wrote(注意:他在列表中错误地标识了 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 的另一个回复(wiki 以表示可信度)回答了这个问题。他wrote:

> 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.

我还询问了“int”在公共内核 API 中的一般用法,his response is there,尽管这不是主题。

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

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

【讨论】:

可能值得注意的是,BLKSSZGET ioctl 返回的变量实际上是unsigned int,正如bdev_logical_block_size(bdev) 返回的那样。 @uncleremus 我现在才看到你的评论。这绝对值得注意。不确定自 2013 年以来发生了多少变化,但值得对当前的 blkdiscard 进行快速代码审查。不过,我的待办事项清单上已经很糟糕了,哈哈。

以上是关于ioctl 参数(例如 0x1268 / BLKSSZGET)实际指定在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

替换 ioctl() 函数

尝试将参数传递给 ioctl 调用将参数归零

ioctl()函数的参数和作用

如何检查 ioctl 上的现有参数

IOCTL:仅在 switch-case 中使用参数 0

fcntl.ioctl() 的 64 位参数