[PATCH v5 0/2] Add support to RTC and HPET timer to LXC containers

This series add support for two timers in LXC containers. It enables sharing some timer devices between host and LXC guest using `timer` settings in XML definition. This series add RTC and HPET timers. Julio Faracco (2): lxc: Add Real Time Clock device into allowed devices lxc: Add HPET device into allowed devices docs/formatdomain.html.in | 4 +- src/lxc/lxc_cgroup.c | 42 +++++++++++++++++++ src/lxc/lxc_controller.c | 88 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) -- 2.24.1

This commit share host Real Time Clock device (rtc) into LXC containers to support hardware clock. This should be available setting up a `rtc` timer under clock section. Since this option is not emulated, it should be available only for `localtime` clock. This option should be readonly due to security reasons. Before: root# hwclock --verbose hwclock from util-linux 2.32.1 System Time: 1581877557.598365 Trying to open: /dev/rtc0 Trying to open: /dev/rtc Trying to open: /dev/misc/rtc No usable clock interface found. hwclock: Cannot access the Hardware Clock via any known method. Now: root# hwclock 2020-02-16 18:23:55.374134+00:00 root# hwclock -w hwclock: ioctl(RTC_SET_TIME) to /dev/rtc to set the time failed: Permission denied Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- docs/formatdomain.html.in | 2 +- src/lxc/lxc_cgroup.c | 33 ++++++++++++++++++++ src/lxc/lxc_controller.c | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 28770188dd..96d06e9c3e 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2465,7 +2465,7 @@ being modified, and can be one of "platform" (currently unsupported), "hpet" (libxl, xen, qemu), "kvmclock" (qemu), - "pit" (qemu), "rtc" (qemu), "tsc" (libxl, qemu - + "pit" (qemu), "rtc" (qemu, lxc), "tsc" (libxl, qemu - <span class="since">since 3.2.0</span>), "hypervclock" (qemu - <span class="since">since 1.2.2</span>) or "armvtimer" (qemu - <span class="since">since 6.1.0</span>). diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 952d021418..065996b3a4 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -333,6 +333,39 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, VIR_CGROUP_DEVICE_RWM) < 0) return -1; + VIR_DEBUG("Allowing timers char devices"); + + /* Sync'ed with Host clock */ + for (i = 0; i < def->clock.ntimers; i++) { + virDomainTimerDefPtr timer = def->clock.timers[i]; + + /* Check if "present" is set to "no" otherwise enable it. */ + if (!timer->present) + continue; + + switch ((virDomainTimerNameType)timer->name) { + case VIR_DOMAIN_TIMER_NAME_PLATFORM: + case VIR_DOMAIN_TIMER_NAME_TSC: + case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: + case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK: + case VIR_DOMAIN_TIMER_NAME_PIT: + case VIR_DOMAIN_TIMER_NAME_HPET: + case VIR_DOMAIN_TIMER_NAME_ARMVTIMER: + case VIR_DOMAIN_TIMER_NAME_LAST: + break; + case VIR_DOMAIN_TIMER_NAME_RTC: + if (virFileExists("/dev/rtc")) { + if (virCgroupAllowDevicePath(cgroup, "/dev/rtc", + VIR_CGROUP_DEVICE_READ, + false) < 0) + return -1; + } else { + VIR_DEBUG("Ignoring non-existent device /dev/rtc"); + } + break; + } + } + VIR_DEBUG("Device whitelist complete"); return 0; diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index fa7b9dd0e7..d97c4ad1b5 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -1530,6 +1530,69 @@ static int virLXCControllerPopulateDevices(virLXCControllerPtr ctrl) } +static int +virLXCControllerSetupTimers(virLXCControllerPtr ctrl) +{ + g_autofree char *path = NULL; + size_t i; + struct stat sb; + virDomainDefPtr def = ctrl->def; + const char *rtc_dev = "/dev/rtc"; + + /* Not sync'ed with Host clock */ + if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) + return 0; + + for (i = 0; i < def->clock.ntimers; i++) { + dev_t dev; + virDomainTimerDefPtr timer = def->clock.timers[i]; + + /* Check if "present" is set to "no" otherwise enable it. */ + if (!timer->present) + continue; + + switch ((virDomainTimerNameType)timer->name) { + case VIR_DOMAIN_TIMER_NAME_PLATFORM: + case VIR_DOMAIN_TIMER_NAME_TSC: + case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: + case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK: + case VIR_DOMAIN_TIMER_NAME_PIT: + case VIR_DOMAIN_TIMER_NAME_HPET: + case VIR_DOMAIN_TIMER_NAME_ARMVTIMER: + case VIR_DOMAIN_TIMER_NAME_LAST: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported timer type (name) '%s'"), + virDomainTimerNameTypeToString(timer->name)); + return -1; + case VIR_DOMAIN_TIMER_NAME_RTC: + if (stat(rtc_dev, &sb) < 0) { + virReportSystemError(errno, _("Unable to access %s"), + rtc_dev); + return -1; + } + + path = g_strdup_printf("/%s/%s.dev/%s", LXC_STATE_DIR, + ctrl->def->name, "/rtc"); + + dev = makedev(major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(path, S_IFCHR, dev) < 0 || + chmod(path, sb.st_mode)) { + virReportSystemError(errno, + _("Failed to make device %s"), + path); + return -1; + } + + if (lxcContainerChown(ctrl->def, path) < 0) + return -1; + break; + } + } + + return 0; +} + + static int virLXCControllerSetupHostdevSubsysUSB(virDomainDefPtr vmDef, virDomainHostdevDefPtr def, @@ -2321,6 +2384,9 @@ virLXCControllerRun(virLXCControllerPtr ctrl) if (virLXCControllerPopulateDevices(ctrl) < 0) goto cleanup; + if (virLXCControllerSetupTimers(ctrl) < 0) + goto cleanup; + if (virLXCControllerSetupAllDisks(ctrl) < 0) goto cleanup; -- 2.24.1

On 2. 3. 2020 1:54, Julio Faracco wrote:
This commit share host Real Time Clock device (rtc) into LXC containers to support hardware clock. This should be available setting up a `rtc` timer under clock section. Since this option is not emulated, it should be available only for `localtime` clock. This option should be readonly due to security reasons.
Before: root# hwclock --verbose hwclock from util-linux 2.32.1 System Time: 1581877557.598365 Trying to open: /dev/rtc0 Trying to open: /dev/rtc Trying to open: /dev/misc/rtc No usable clock interface found. hwclock: Cannot access the Hardware Clock via any known method.
Now: root# hwclock 2020-02-16 18:23:55.374134+00:00 root# hwclock -w hwclock: ioctl(RTC_SET_TIME) to /dev/rtc to set the time failed: Permission denied
Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- docs/formatdomain.html.in | 2 +- src/lxc/lxc_cgroup.c | 33 ++++++++++++++++++++ src/lxc/lxc_controller.c | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-)
On my system, the /dev/rtc is just a symlink to /dev/rtc0. Should we create the symlink too or is it okay to just create /dev/rtc? Otherwise looking good. Michal

Hi Michal, /dev/rtc symlink is created by udev default rules. Maybe we can check for both: /dev/rtc and /dev/rtc0. -- Julio Cesar Faracco Em seg., 16 de mar. de 2020 às 15:16, Michal Prívozník <mprivozn@redhat.com> escreveu:
On 2. 3. 2020 1:54, Julio Faracco wrote:
This commit share host Real Time Clock device (rtc) into LXC containers to support hardware clock. This should be available setting up a `rtc` timer under clock section. Since this option is not emulated, it should be available only for `localtime` clock. This option should be readonly due to security reasons.
Before: root# hwclock --verbose hwclock from util-linux 2.32.1 System Time: 1581877557.598365 Trying to open: /dev/rtc0 Trying to open: /dev/rtc Trying to open: /dev/misc/rtc No usable clock interface found. hwclock: Cannot access the Hardware Clock via any known method.
Now: root# hwclock 2020-02-16 18:23:55.374134+00:00 root# hwclock -w hwclock: ioctl(RTC_SET_TIME) to /dev/rtc to set the time failed: Permission denied
Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- docs/formatdomain.html.in | 2 +- src/lxc/lxc_cgroup.c | 33 ++++++++++++++++++++ src/lxc/lxc_controller.c | 66 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-)
On my system, the /dev/rtc is just a symlink to /dev/rtc0. Should we create the symlink too or is it okay to just create /dev/rtc?
Otherwise looking good.
Michal

On 17. 3. 2020 14:41, Julio Faracco wrote:
Hi Michal,
/dev/rtc symlink is created by udev default rules. Maybe we can check for both: /dev/rtc and /dev/rtc0.
I think it's safe to rely on default udev behavior and just create rtc0. What do you think? No need to send another version, I can do the fixes locally before pushing. Michal

I'm good with that. I verified other resources that use rtc... they are checking for rtc0. Thanks, Michal -- Julio Cesar Faracco Em ter., 17 de mar. de 2020 às 12:32, Michal Prívozník <mprivozn@redhat.com> escreveu:
On 17. 3. 2020 14:41, Julio Faracco wrote:
Hi Michal,
/dev/rtc symlink is created by udev default rules. Maybe we can check for both: /dev/rtc and /dev/rtc0.
I think it's safe to rely on default udev behavior and just create rtc0. What do you think? No need to send another version, I can do the fixes locally before pushing.
Michal

On 17. 3. 2020 17:39, Julio Faracco wrote:
I'm good with that. I verified other resources that use rtc... they are checking for rtc0. Thanks, Michal
Cool, I've fixed patches (actually I did slightly change them to make it easier to add new timer) and pushed. Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal

This commit is related to RTC timer device too. HPET is being shared from host device through `localtime` clock. This timer is available creating a new timer using `hpet` name. Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- docs/formatdomain.html.in | 2 +- src/lxc/lxc_cgroup.c | 11 ++++++++++- src/lxc/lxc_controller.c | 24 +++++++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 96d06e9c3e..d1d2955e51 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -2464,7 +2464,7 @@ The <code>name</code> attribute selects which timer is being modified, and can be one of "platform" (currently unsupported), - "hpet" (libxl, xen, qemu), "kvmclock" (qemu), + "hpet" (libxl, xen, qemu, lxc), "kvmclock" (qemu), "pit" (qemu), "rtc" (qemu, lxc), "tsc" (libxl, qemu - <span class="since">since 3.2.0</span>), "hypervclock" (qemu - <span class="since">since 1.2.2</span>) or diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c index 065996b3a4..cb5d0da980 100644 --- a/src/lxc/lxc_cgroup.c +++ b/src/lxc/lxc_cgroup.c @@ -349,7 +349,6 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK: case VIR_DOMAIN_TIMER_NAME_PIT: - case VIR_DOMAIN_TIMER_NAME_HPET: case VIR_DOMAIN_TIMER_NAME_ARMVTIMER: case VIR_DOMAIN_TIMER_NAME_LAST: break; @@ -363,6 +362,16 @@ static int virLXCCgroupSetupDeviceACL(virDomainDefPtr def, VIR_DEBUG("Ignoring non-existent device /dev/rtc"); } break; + case VIR_DOMAIN_TIMER_NAME_HPET: + if (virFileExists("/dev/hpet")) { + if (virCgroupAllowDevicePath(cgroup, "/dev/hpet", + VIR_CGROUP_DEVICE_READ, + false) < 0) + return -1; + } else { + VIR_DEBUG("Ignoring non-existent device /dev/hpet"); + } + break; } } diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c index d97c4ad1b5..8dcfd4c97e 100644 --- a/src/lxc/lxc_controller.c +++ b/src/lxc/lxc_controller.c @@ -1538,6 +1538,7 @@ virLXCControllerSetupTimers(virLXCControllerPtr ctrl) struct stat sb; virDomainDefPtr def = ctrl->def; const char *rtc_dev = "/dev/rtc"; + const char *hpet_dev = "/dev/hpet"; /* Not sync'ed with Host clock */ if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) @@ -1557,7 +1558,6 @@ virLXCControllerSetupTimers(virLXCControllerPtr ctrl) case VIR_DOMAIN_TIMER_NAME_KVMCLOCK: case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK: case VIR_DOMAIN_TIMER_NAME_PIT: - case VIR_DOMAIN_TIMER_NAME_HPET: case VIR_DOMAIN_TIMER_NAME_ARMVTIMER: case VIR_DOMAIN_TIMER_NAME_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -1583,6 +1583,28 @@ virLXCControllerSetupTimers(virLXCControllerPtr ctrl) return -1; } + if (lxcContainerChown(ctrl->def, path) < 0) + return -1; + break; + case VIR_DOMAIN_TIMER_NAME_HPET: + if (stat(hpet_dev, &sb) < 0) { + virReportSystemError(errno, _("Unable to access %s"), + hpet_dev); + return -1; + } + + path = g_strdup_printf("/%s/%s.dev/%s", LXC_STATE_DIR, + ctrl->def->name, "/hpet"); + + dev = makedev(major(sb.st_rdev), minor(sb.st_rdev)); + if (mknod(path, S_IFCHR, dev) < 0 || + chmod(path, sb.st_mode)) { + virReportSystemError(errno, + _("Failed to make device %s"), + path); + return -1; + } + if (lxcContainerChown(ctrl->def, path) < 0) return -1; break; -- 2.24.1

On 2. 3. 2020 1:54, Julio Faracco wrote:
This commit is related to RTC timer device too. HPET is being shared from host device through `localtime` clock. This timer is available creating a new timer using `hpet` name.
Signed-off-by: Julio Faracco <jcfaracco@gmail.com> --- docs/formatdomain.html.in | 2 +- src/lxc/lxc_cgroup.c | 11 ++++++++++- src/lxc/lxc_controller.c | 24 +++++++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-)
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Julio Faracco
-
Michal Prívozník