clinux-kernelioctlblock-device

Block device driver - Understanding received ioctl


I just implemented a virtual block device and as I was playing with it I seen and strange behaviour related to ioctls.

My device is just a memory zone divided in two 512 bytes sectors. I can currently read/write from/to it at a specific offset using system calls.

The device is also protected against concurrent write access. It accept an unlimited number of reader but only one writter at time and only if nobody is currently reading from it.

Ioctls aren't handled yet but despite of it I was logging related informations.

int block_mod_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
{
    DEBUG("Entering IOCTL handling function\n");
    switch(cmd)
    {
        default:
            WARNING("Unknown ioctl call: %x\n", cmd);
            WARNING("Ioctl: type=%x\tnumber=%x\tdirection=%x\tsize=%x\n", _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));
            return -1;
    }
    return 0;
}

I was playing with dd and I found a strange behaviour that I don't understand after some digging around.

# dd if=/dev/blkmodtest of=file.out seek=10
[  107.367797] Oppened in read only mode
[  107.368595] Read access: 1   Write access: 0
[  107.370367] Reading from device
[  107.372939] Closed read only mode
[  107.373690] Read access: 0   Write access: 0
2+0 records in
2+0 records out
1024 bytes (1.0 kB, 1.0 KiB) copied, 0.00584625 s, 175 kB/s

# dd if=/dev/blkmodtest of=file.out skip=10
[  111.982493] Oppened in read only mode
[  111.983326] Read access: 1   Write access: 0
[  111.985247] Unknown ioctl call: 80306d02
[  111.986096] Ioctl: type=6d   number=2    direction=2 size=30
[  111.987618] Unknown ioctl call: 80306d02
[  111.988436] Ioctl: type=6d   number=2    direction=2 size=30
dd: /dev/blkmodtest: cannot skip: Invalid argument
[  111.991032] Closed read only mode
[  111.991705] Read access: 0   Write access: 0
0+0 records in
0+0 records out
0 bytes copied, 0.00783969 s, 0.0 kB/s

It seems that dd is issuing an ioctl call that my driver can't (obviously) handle. I know that I should not give skip=10 but seek=10 as I'm reading from the device but it was for test purpose.

I'm currently trying to understand why this ioctl is issued and for what purpose?

If I understand well the documentation I have found, the ioctl type is 'm' which should be described into one of the following file (source):

'm' 00-09   linux/mmtimer.h     conflict!
'm' all linux/mtio.h        conflict!
'm' all linux/soundcard.h   conflict!
'm' all linux/synclink.h    conflict!
'm' 00-19   drivers/message/fusion/mptctl.h conflict!
'm' 00 drivers/scsi/megaraid/megaraid_ioctl.h conflict!

I have checked those files but I can't find useful information which would help me to go further into understanding what's happening in that specific case.


Solution

  • Setting up a loop device (sudo losetup loop0 /path/to/some/image), the output of strace dd if=/dev/loop0 of=out.data skip=10 bs=40 count=2 contains

    open("/dev/loop0", O_RDONLY)            = 3
    dup2(3, 0)                              = 0
    close(3)                                = 0
    lseek(0, 0, SEEK_CUR)                   = 0
    ioctl(0, MTIOCGET, 0x7fffac670080)      = -1 EINVAL (Invalid argument)
    lseek(0, 400, SEEK_CUR)                 = 400
    fstat(0, {st_mode=S_IFBLK|0660, st_rdev=makedev(7, 0), ...}) = 0
    

    which indicates dd is trying to do an MTIOCGET ioctl on your block device. man 4 st shows

    The st driver provides the interface to a variety of SCSI tape devices.
    [...]
    MTIOCGET — get status
    This request takes an argument of type (struct mtget *).

    In other words, dd suspects your block device might be some variety of SCSI tape device, and asks it for the status using the ioctl() you are seeing.

    It is perfectly okay for your driver to return EINVAL for this; that's what e.g. loop devices do, too.