I have an IOBlockStorageDevice device created, which reports the blocksize, as set by the user. It creates a /dev/diskX entry for the disk. If reportBlockSize returns 4096, and an attempt to write a single block is made, it writes exactly 1 block.
If blocksize of 512 is used, a single block write request becomes 8 block read, followed by 8 block write. (as observed in doAsyncReadWrite).
I believe I have tracked this down to spec_vnops.c spec_write()
543 devBlockSize = vp->v_specsize;
544 if (devBlockSize > PAGE_SIZE)
545 return(EINVAL);
546
547 bscale = PAGE_SIZE / devBlockSize;
548 blkmask = bscale - 1;
549 bsize = bscale * devBlockSize;
If a block is smaller than PAGE_SIZE(4096) then make bsize be 4096, and in a conditional statement below, it decides to call read first, then write.
Worse is if you try to set a blockSize larger than 4096, it just straight up fails.
This seems rather limited, and I am wondering if there is a way to avoid using specfs. Since I create my device using IOkit, I assume it sets the vnops to specfs somewhere deep inside. So even if I were to make my own specfs vnops, I have no way to set them?
Once a filesystem is mounted on the device, it will use different vnops and all is well. But it is hard to even partition the device, if for example, blocksize is 8192.