Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion over ioctl() and kernel headers

As far as I know, ioctl() is used to expose an "extended" system call interface to userspace applications. Rather than adding thousands of system calls that are unique to specific drivers, ioctl() is used to provide extensible driver-specific functions through a single system call.

This seems clear enough. However, I'm trying to compile my first application that uses an ioctl() call, and I'm starting to doubt my understanding.

Specifically, I want to make an ioctl() call to "sanitize" an eMMC device. Taking a look at /usr/include/linux/mmc/ioctl.h (or in kernel source at include/uapi/linux/mmc/ioctl.h), I can see this structure:

struct mmc_ioc_cmd {
        // Most fields omitted
        int write_flag;    
        __u32 opcode;
        __u32 arg;
};

From userspace, I don't have any issues including this header and passing this structure into my ioctl() calls.

So this is what my final sanitize snippet looks like:

int sanitize(int fd)
{
  struct mmc_ioc_cmd command;
  memset(&command, 0, sizeof(command));

  command.write_flag = 1;
  command.opcode = MMC_SWITCH;
  command.arg = EXT_CSD_SANITIZE_START << 16;

  return ioctl(fd, MMC_IOC_CMD, &command);
}

My problem is that MMC_SWITCH and EXT_CSD_SANITIZE_START are both defined in kernel headers. Specifically, in my kernel source, they're both found at include/linux/mmc/mmc.h.

Everything I've seen on the internet says to not include headers from the kernel source when building userspace projects. If that's the case, how can you reasonably use the MMC ioctl()? The kernel exposes the structure to pass into ioctl(), but it seems like you can only use the structure by filling it in with "hidden" constants hidden in the kernel source.

My current solution is to copy the necessary constants from the kernel headers to my own project, but this feels dirty to me.

Am I misunderstanding the use-case for ioctl()? Is this a design oversight?

like image 724
Matt K Avatar asked Oct 18 '22 22:10

Matt K


1 Answers

The MMC_IOC_CMD ioctl, and the corresponding mmc_ioc_cmd structure, are part of the Linux userspace API, and therefore are defined in the uapi headers that are installed into /usr/include.

The value that you put into the opcode field gets sent directly to the device. The kernel does not really care what it is, and cannot guarantee what opcodes the device supports, or how it behaves for any specific opcode. Therefore, opcodes like MMC_SWITCH are not part of the API.

As far as I can see, you are supposed to get the opcodes from the relevant MMC standards.

(This is not really a good reason to keep these symbols out of the user-space API; copying the kernel header is much easier than manually transcribing the values from the standard. And the kernel actually has a special case for handling EXT_CSD_SANITIZE_START through this ioctl.)

like image 63
CL. Avatar answered Oct 30 '22 14:10

CL.