Avoiding ioctl version mismatch: libdevmapper approach
bmr-cymru opened this issue · 3 comments
If the running kernel has an older device-mapper ioctl version than the headers used to build devicemapper-rs tests will fail with messages like the following in dmesg:
device-mapper: ioctl: ioctl interface mismatch: kernel(4.45.0), user(4.46.0), cmd(2)
This is because devicemapper-rs passes the exact version of the interface it was built against with every ioctl command. The libdevmapper library uses a table indexed by the ioctl command and sets the version in which that specific command was introduced:
static struct cmd_data _cmd_data_v4[] = {
{"create", DM_DEV_CREATE, {4, 0, 0}},
{"reload", DM_TABLE_LOAD, {4, 0, 0}},
{"remove", DM_DEV_REMOVE, {4, 0, 0}},
{"remove_all", DM_REMOVE_ALL, {4, 0, 0}},
{"suspend", DM_DEV_SUSPEND, {4, 0, 0}},
{"resume", DM_DEV_SUSPEND, {4, 0, 0}},
{"info", DM_DEV_STATUS, {4, 0, 0}},
{"deps", DM_TABLE_DEPS, {4, 0, 0}},
{"rename", DM_DEV_RENAME, {4, 0, 0}},
{"version", DM_VERSION, {4, 0, 0}},
{"status", DM_TABLE_STATUS, {4, 0, 0}},
{"table", DM_TABLE_STATUS, {4, 0, 0}},
{"waitevent", DM_DEV_WAIT, {4, 0, 0}},
{"names", DM_LIST_DEVICES, {4, 0, 0}},
{"clear", DM_TABLE_CLEAR, {4, 0, 0}},
{"mknodes", DM_DEV_STATUS, {4, 0, 0}},
#ifdef DM_LIST_VERSIONS
{"versions", DM_LIST_VERSIONS, {4, 1, 0}},
#endif
#ifdef DM_TARGET_MSG
{"message", DM_TARGET_MSG, {4, 2, 0}},
#endif
#ifdef DM_DEV_SET_GEOMETRY
{"setgeometry", DM_DEV_SET_GEOMETRY, {4, 6, 0}},
#endif
#ifdef DM_DEV_ARM_POLL
{"armpoll", DM_DEV_ARM_POLL, {4, 36, 0}},
#endif
#ifdef DM_GET_TARGET_VERSION
{"target-version", DM_GET_TARGET_VERSION, {4, 41, 0}},
#endif
};
This is then copied from the table when initialising the version member of the ioctl header:
version = &_cmd_data_v4[dmt->type].version;
dmi->version[0] = (*version)[0];
dmi->version[1] = (*version)[1];
dmi->version[2] = (*version)[2];
This would make the version requirements much more relaxed - rather than always requiring the latest ioctl version that the library was built against we would only require at a minimum a version supporting all commands actually used in the current version.
@bmr-cymru I think we could call this the recommended approach. If we could get this in in 0.32.1[1], i.e., before your cookie work, that would work out well. It looks like it would require a little re-architecting w/in the dm.rs file, but otherwise I feel that the changes would be self-contained.
[1] #762
I have something working for this now, and strace confirms that it's doing the right thing and setting the version appropriately, e.g.:
ioctl(4, DM_DEV_CREATE, {version=[4, 0, 0], data_size=16384, name="pool_dm-rs_test_delme", flags=0} => {version=[4, 46, 0], data_size=305, dev=makedev(0xfd, 0x3), name="pool_dm-rs_test_delme
", target_count=0, open_count=0, event_nr=0, flags=0}) = 0
ioctl(4, DM_TABLE_LOAD, {version=[4, 0, 0], data_size=16384, data_start=312, name="pool_dm-rs_test_delme", target_count=1, flags=0, ...} => {version=[4, 46, 0], data_size=305, data_start=312
, dev=makedev(0xfd, 0x3), name="pool_dm-rs_test_delme", target_count=0, open_count=0, event_nr=0, flags=DM_INACTIVE_PRESENT_FLAG}) = 0
ioctl(4, DM_DEV_SUSPEND, {version=[4, 0, 0], data_size=16384, name="pool_dm-rs_test_delme", event_nr=0, flags=0} => {version=[4, 46, 0], data_size=305, dev=makedev(0xfd, 0x3), name="pool_dm-
rs_test_delme", target_count=1, open_count=0, event_nr=0, flags=DM_ACTIVE_PRESENT_FLAG|DM_UEVENT_GENERATED_FLAG}) = 0
ioctl(4, DM_TARGET_MSG, {version=[4, 2, 0], data_size=16384, data_start=312, name="pool_dm-rs_test_delme", flags=0, ...} => {version=[4, 46, 0], data_size=305, data_start=312, dev=makedev(0xfd, 0x3), name="pool_dm-rs_test_delme", target_count=1, open_count=0, event_nr=0, flags=DM_ACTIVE_PRESENT_FLAG}) = 0
ioctl(4, DM_LIST_DEVICES, {version=[4, 0, 0], data_size=16384, data_start=312, flags=0} => {version=[4, 46, 0], data_size=496, data_start=312, flags=0, ...}) = 0
ioctl(4, DM_DEV_CREATE, {version=[4, 0, 0], data_size=16384, name="name_dm-rs_test_delme", flags=0} => {version=[4, 46, 0], data_size=305, dev=makedev(0xfd, 0x4), name="name_dm-rs_test_delme", target_count=0, open_count=0, event_nr=0, flags=0}) = 0
Note the version=
array passed to each ioctl - for DM_DEV_CREATE, DM_TABLE_LOAD etc. the version is set to 4.0.0, whereas the later DM_TARGET_MSG command receives a version of 4.2.0.
I'll push a branch shortly with my initial prototype - any suggestions on better ways to implement the cmd/version lookup would be welcome. I'm also trying to decide whether to allow unknown commands through (with the maximum version known to the bindings), or whether to treat those as an error. The current version of the code does the former but as I think about it I'm more inclined to treat that as an error (it would require manual inspection and modification of the table to permit new DM commands introduced in the kernel/libdevmapper, but I think that's desirable).