[PATCH v4 0/4] Add support to enable/disable hotplug on pci-root controller
by Ani Sinha
changelog:
v4: split the original patchset into a pci-root controller specific patch series.
also the libvirt conf is now a sub-element of the pci-root controller as was
suggested by Dan Berrange. Please see discussion here:
https://listman.redhat.com/archives/libvir-list/2021-September/msg00839.html
v3: reorganized the patches as per Laine's suggestion. Added more
details in commit messages. Added conf description in formatdomain.rst.
Added changelog for next release.
v2: fixed bugs and added additional missing unit tests.
v1: initial implementation. Had some bugs and missed some unit tests
This patchset introduces libvirt xml support to enable/disable hotplug on the
pci-root controller. It adds a 'target' subelement for the pci-root controller
with a 'hotplug' property. This property can be used to enable or disable
hotplug for the pci-root controller. For example, in order to disable hotplug
on the pci-root controller, one has to use set '<target hotplug='off'>' as
shown below:
<controller type='pci' model='pci-root'>
<target hotplug='off'/>
</controller>
'<target hotplug='on'>' option would enable hotplug for pci-root controller.
This is also the default value. This option is only available for pc machine
types and is applicable for qemu only/kvm accelerator onlt.This feature was
introduced from qemu version 5.2 with the following change in qemu repository:
3d7e78aa7777f ("Introduce a new flag for i440fx to disable PCI hotplug on the root bus")
The above qemu commit describes some reasons why users might to disable hotplug
on PCI root buses [1].
The corresponding commandline option to qemu for x86 guests is:
-global PIIX4_PM.acpi-root-pci-hotplug=<off/on>
Notes:
1. The use case scenario described by Laine in
https://listman.redhat.com/archives/libvir-list/2020-February/msg00110.html
intentionally does not discuss i440fx and focusses solely on q35. I do realize
that redhat has moved on from i440fx and currently efforts for new features
are concentrated on q35 machines only. We have had some hard debates on this
on the qemu mailing list before. The fact of the matter is that i440fx is
not at 1-1 parity with q35. There are many users who are currenly using i440fx
and are simply not ready to move to q35 without sacrificing some
existing features they support today. For example
https://wiki.qemu.org/images/4/4e/Q35.pdf lists some of q35 limitations.
https://www.linux-kvm.org/images/0/06/2012-forum-Q35.pdf provides more
information on the differences. Hence we need to solve the issue Laine has
described in the above email for i440fx without adding additional bridges.
Further, in Daniel Berrange's words from :
https://lists.gnu.org/archive/html/qemu-devel/2020-04/msg03012.html
"From the upstream POV, there's been no decision / agreement to phase
out PIIX, this is purely a RHEL downstream decision & plan. If other
distros / users have a different POV, and find the feature useful, we
should accept the patch if it meets the normal QEMU patch requirements.
"
Also to be noted that I have already experimented this qemu commandline option
using libvirt passthrough feature as has been documented in
http://blog.vmsplice.net/2011/04/how-to-pass-qemu-command-line-options.html
This was only meant to be a short term solution until libvirt started
supporting this natively. Supporting this option through libvirt would simplify
their use case as well as add capability validations
and graceful failure scenarios in case qemu did not support the option.
Ani Sinha (4):
qemu: capablities: detect presence of acpi-root-pci-hotplug for i440fx
machines
conf: introduce option to enable/disable pci hotplug on pci-root
controller
qemu: command: add support to enable/disable hotplug on pci-root
controller
NEWS: release note the new hotplug enable/disable option on pci-root
controller
NEWS.rst | 6 ++++
docs/formatdomain.rst | 12 ++++---
docs/schemas/domaincommon.rng | 10 ++++++
src/qemu/qemu_capabilities.c | 4 +++
src/qemu/qemu_capabilities.h | 3 ++
src/qemu/qemu_command.c | 12 +++++++
src/qemu/qemu_validate.c | 9 +++++-
.../caps_5.2.0.x86_64.xml | 1 +
.../caps_6.0.0.x86_64.xml | 1 +
.../caps_6.1.0.x86_64.xml | 1 +
.../pc-i440fx-acpi-root-hotplug-disable.args | 31 +++++++++++++++++++
.../pc-i440fx-acpi-root-hotplug-disable.xml | 17 ++++++++++
.../pc-i440fx-acpi-root-hotplug-enable.err | 1 +
.../pc-i440fx-acpi-root-hotplug-enable.xml | 17 ++++++++++
tests/qemuxml2argvtest.c | 6 ++++
.../pc-i440fx-acpi-root-hotplug-disable.xml | 30 ++++++++++++++++++
tests/qemuxml2xmltest.c | 2 ++
17 files changed, 157 insertions(+), 6 deletions(-)
create mode 100644 tests/qemuxml2argvdata/pc-i440fx-acpi-root-hotplug-disable.args
create mode 100644 tests/qemuxml2argvdata/pc-i440fx-acpi-root-hotplug-disable.xml
create mode 100644 tests/qemuxml2argvdata/pc-i440fx-acpi-root-hotplug-enable.err
create mode 100644 tests/qemuxml2argvdata/pc-i440fx-acpi-root-hotplug-enable.xml
create mode 100644 tests/qemuxml2xmloutdata/pc-i440fx-acpi-root-hotplug-disable.xml
--
2.25.1
3 years, 3 months
[PATCH] qemu: ingore the transient domain state in fake reboot
by Zhenzhong Duan
When action for 'on_poweroff' is set to 'restart', 'fake reboot'
is triggered and qemu shutdown state is transient. Domain state
need not to be changed and events not sent in this case.
Fixes:4ffc807214cb80086d57e1d3e7b60959a41d2874
Signed-off-by: Zhenzhong Duan <zhenzhong.duan(a)intel.com>
---
src/qemu/qemu_process.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 632cb817b9..fcdda4ffe1 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -612,7 +612,7 @@ qemuProcessHandleShutdown(qemuMonitor *mon G_GNUC_UNUSED,
/* In case of fake reboot qemu shutdown state is transient so don't
* change domain state nor send events. */
- if (!priv->fakeReboot ||
+ if (!priv->fakeReboot &&
vm->def->onPoweroff != VIR_DOMAIN_LIFECYCLE_ACTION_RESTART) {
VIR_DEBUG("Transitioned guest %s to shutdown state",
vm->def->name);
--
2.25.1
3 years, 3 months
[libvirt PATCH 0/4] qemu: deprecate more capabilities
by Ján Tomko
Applies on top of my seccomp sandbox series.
Ján Tomko (4):
qemu: assume QEMU_CAPS_MACHINE_KERNEL_IRQCHIP
qemu: Deprecate QEMU_CAPS_MACHINE_KERNEL_IRQCHIP
qemu: assume QEMU_CAPS_FSDEV_CREATEMODE
qemu: deprecate QEMU_CAPS_FSDEV_CREATEMODE
src/qemu/qemu_capabilities.c | 6 ++----
src/qemu/qemu_capabilities.h | 4 ++--
src/qemu/qemu_validate.c | 12 ------------
tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_2.11.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_3.0.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_3.0.0.riscv32.xml | 2 --
tests/qemucapabilitiesdata/caps_3.0.0.riscv64.xml | 2 --
tests/qemucapabilitiesdata/caps_3.0.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_3.0.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_3.1.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_3.1.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.2.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.2.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_4.2.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_4.2.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.0.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.0.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.0.0.riscv64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.0.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.1.0.sparc.xml | 2 --
tests/qemucapabilitiesdata/caps_5.1.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.2.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.2.0.ppc64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.2.0.riscv64.xml | 2 --
tests/qemucapabilitiesdata/caps_5.2.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_5.2.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml | 2 --
tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml | 2 --
tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml | 2 --
tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml | 2 --
42 files changed, 4 insertions(+), 96 deletions(-)
--
2.31.1
3 years, 3 months
[libvirt PATCH 0/5] qemu: simplify seccomp sandbox
by Ján Tomko
Inspired by Peter's q-c-l-o cleanups.
Ján Tomko (5):
qemu: always assume QEMU_CAPS_SECCOMP_BLACKLIST
qemu: conf: simplify seccomp_sandbox comment
qemu: seccomp: remove dead code
qemu: capabilities: deprecate QEMU_CAPS_SECCOMP_BLACKLIST
qemu: capabilities: do not look at parameters for sandbox
src/qemu/qemu.conf | 11 +++++------
src/qemu/qemu_capabilities.c | 5 ++---
src/qemu/qemu_capabilities.h | 2 +-
src/qemu/qemu_command.c | 7 +------
tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_2.11.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_3.0.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_3.0.0.riscv32.xml | 1 -
tests/qemucapabilitiesdata/caps_3.0.0.riscv64.xml | 1 -
tests/qemucapabilitiesdata/caps_3.0.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_3.0.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_3.1.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_3.1.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.riscv32.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.riscv64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_4.0.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.1.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.2.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.2.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_4.2.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_4.2.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.0.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.0.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.0.0.riscv64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.0.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.1.0.sparc.xml | 1 -
tests/qemucapabilitiesdata/caps_5.1.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.2.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.2.0.ppc64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.2.0.riscv64.xml | 1 -
tests/qemucapabilitiesdata/caps_5.2.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_5.2.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml | 1 -
tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml | 1 -
tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml | 1 -
tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml | 1 -
43 files changed, 9 insertions(+), 55 deletions(-)
--
2.31.1
3 years, 3 months
[PATCH v2 0/7] virsh: domain: small refactoring
by Kristina Hanicova
This is partially v2 of:
https://listman.redhat.com/archives/libvir-list/2021-September/msg00728.html
Diff to v1:
* split changes into smaller patches
Kristina Hanicova (7):
virsh: domain: use early return in virshDomainDefine()
virsh: domain: remove nested 'if' in cmdAttachDisk()
virsh: domain: remove 'ret' variable and use direct return when
possible
virsh: domain: remove 'ret' variable, use early return when possible
virsh: domain: use early return when possible
virsh: domain: remove 'ret' variable and use 'count' instead
virsh: domain: remove else branch
tools/virsh-domain.c | 232 +++++++++++++++++--------------------------
1 file changed, 92 insertions(+), 140 deletions(-)
--
2.31.1
3 years, 3 months
[PATCH v3 0/3] remove sysconfig files
by Olaf Hering
rebased to 919f25d36ef0ea41c50bdb5afa0b83187ffb3c87
Olaf Hering (3):
libvirt.spec: relocate pre script of daemon-driver-qemu
remove sysconfig files
NEWS: mention removal of sysconfig
NEWS.rst | 10 +++
docs/daemons.rst | 20 +++++
docs/remote.html.in | 2 +-
libvirt.spec.in | 100 ++++++++++++++++--------
src/ch/meson.build | 5 --
src/ch/virtchd.service.in | 1 +
src/ch/virtchd.sysconf | 3 -
src/interface/meson.build | 5 --
src/interface/virtinterfaced.service.in | 1 +
src/interface/virtinterfaced.sysconf | 3 -
src/libxl/meson.build | 5 --
src/libxl/virtxend.service.in | 1 +
src/libxl/virtxend.sysconf | 3 -
src/locking/meson.build | 5 --
src/locking/virtlockd.service.in | 1 +
src/locking/virtlockd.sysconf | 3 -
src/logging/meson.build | 5 --
src/logging/virtlogd.sysconf | 3 -
src/lxc/meson.build | 5 --
src/lxc/virtlxcd.service.in | 1 +
src/lxc/virtlxcd.sysconf | 3 -
src/meson.build | 16 ----
src/network/meson.build | 5 --
src/network/virtnetworkd.service.in | 1 +
src/network/virtnetworkd.sysconf | 3 -
src/node_device/meson.build | 5 --
src/node_device/virtnodedevd.service.in | 1 +
src/node_device/virtnodedevd.sysconf | 3 -
src/nwfilter/meson.build | 5 --
src/nwfilter/virtnwfilterd.service.in | 1 +
src/nwfilter/virtnwfilterd.sysconf | 3 -
src/qemu/meson.build | 5 --
src/qemu/virtqemud.service.in | 7 ++
src/qemu/virtqemud.sysconf | 12 ---
src/remote/libvirtd.service.in | 7 ++
src/remote/libvirtd.sysconf | 21 -----
src/remote/meson.build | 10 ---
src/remote/virtproxyd.service.in | 1 +
src/remote/virtproxyd.sysconf | 3 -
src/secret/meson.build | 5 --
src/secret/virtsecretd.service.in | 1 +
src/secret/virtsecretd.sysconf | 3 -
src/storage/meson.build | 5 --
src/storage/virtstoraged.service.in | 1 +
src/storage/virtstoraged.sysconf | 3 -
src/vbox/meson.build | 5 --
src/vbox/virtvboxd.service.in | 1 +
src/vbox/virtvboxd.sysconf | 3 -
src/vz/meson.build | 5 --
src/vz/virtvzd.service.in | 1 +
src/vz/virtvzd.sysconf | 3 -
tools/libvirt-guests.sh.in | 40 ++++++++++
tools/libvirt-guests.sysconf | 50 ------------
tools/meson.build | 6 --
54 files changed, 166 insertions(+), 260 deletions(-)
delete mode 100644 src/ch/virtchd.sysconf
delete mode 100644 src/interface/virtinterfaced.sysconf
delete mode 100644 src/libxl/virtxend.sysconf
delete mode 100644 src/locking/virtlockd.sysconf
delete mode 100644 src/logging/virtlogd.sysconf
delete mode 100644 src/lxc/virtlxcd.sysconf
delete mode 100644 src/network/virtnetworkd.sysconf
delete mode 100644 src/node_device/virtnodedevd.sysconf
delete mode 100644 src/nwfilter/virtnwfilterd.sysconf
delete mode 100644 src/qemu/virtqemud.sysconf
delete mode 100644 src/remote/libvirtd.sysconf
delete mode 100644 src/remote/virtproxyd.sysconf
delete mode 100644 src/secret/virtsecretd.sysconf
delete mode 100644 src/storage/virtstoraged.sysconf
delete mode 100644 src/vbox/virtvboxd.sysconf
delete mode 100644 src/vz/virtvzd.sysconf
delete mode 100644 tools/libvirt-guests.sysconf
3 years, 3 months
[PATCH] vsh: Don't check for OOM in vshGetTypedParamValue()
by Michal Privoznik
Both function description and function itself mention check for
OOM which can't happen really. There was a bug in glib where
g_strdup_*() might have not aborted on OOM, but we have our own
implementation when dealing with broken glib (see
vir_g_strdup_printf()). Therefore, checking for OOM is redundant
and can never be true.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
tools/vsh.c | 25 +++++++++----------------
1 file changed, 9 insertions(+), 16 deletions(-)
diff --git a/tools/vsh.c b/tools/vsh.c
index e81369f224..189c452f73 100644
--- a/tools/vsh.c
+++ b/tools/vsh.c
@@ -1806,51 +1806,44 @@ vshCommandOptTimeoutToMs(vshControl *ctl, const vshCmd *cmd, int *timeout)
* ---------------
*/
-/* Return a non-NULL string representation of a typed parameter; exit
- * if we are out of memory. */
+/* Return a non-NULL string representation of a typed parameter; exit on
+ * unknown type. */
char *
vshGetTypedParamValue(vshControl *ctl, virTypedParameterPtr item)
{
- char *str = NULL;
-
switch (item->type) {
case VIR_TYPED_PARAM_INT:
- str = g_strdup_printf("%d", item->value.i);
+ return g_strdup_printf("%d", item->value.i);
break;
case VIR_TYPED_PARAM_UINT:
- str = g_strdup_printf("%u", item->value.ui);
+ return g_strdup_printf("%u", item->value.ui);
break;
case VIR_TYPED_PARAM_LLONG:
- str = g_strdup_printf("%lld", item->value.l);
+ return g_strdup_printf("%lld", item->value.l);
break;
case VIR_TYPED_PARAM_ULLONG:
- str = g_strdup_printf("%llu", item->value.ul);
+ return g_strdup_printf("%llu", item->value.ul);
break;
case VIR_TYPED_PARAM_DOUBLE:
- str = g_strdup_printf("%f", item->value.d);
+ return g_strdup_printf("%f", item->value.d);
break;
case VIR_TYPED_PARAM_BOOLEAN:
- str = g_strdup(item->value.b ? _("yes") : _("no"));
+ return g_strdup(item->value.b ? _("yes") : _("no"));
break;
case VIR_TYPED_PARAM_STRING:
- str = g_strdup(item->value.s);
+ return g_strdup(item->value.s);
break;
default:
vshError(ctl, _("unimplemented parameter type %d"), item->type);
- }
-
- if (!str) {
- vshError(ctl, "%s", _("Out of memory"));
exit(EXIT_FAILURE);
}
- return str;
}
void
--
2.32.0
3 years, 3 months
[PATCH v2 0/6] virsh: refactor some bigger functions
by Kristina Hanicova
This is v2 of patch:
https://listman.redhat.com/archives/libvir-list/2021-September/msg00730.html
Diff to v1:
* split the previous patch into more smaller ones to make the code
review easier
* small code quality alternations I did not notice were possible before
This series refactors a few bigger functions mainly trying to remove too
deep indentation and make them more readable and more consistent with
the rest of the files.
Kristina Hanicova (6):
virsh: domain: refactor cmdSchedinfo()
virsh: domain: refactor virshCPUCountCollect()
virsh: domain: refactor cmdLxcEnterNamespace()
virsh: host: refactor cmdFreecell()
virsh: host: refactor cmdNodeCpuStats()
virsh: volume: refactor cmdVolInfo()
tools/virsh-domain.c | 210 ++++++++++++++++++++-----------------------
tools/virsh-host.c | 139 ++++++++++++++--------------
tools/virsh-volume.c | 52 +++++------
3 files changed, 189 insertions(+), 212 deletions(-)
--
2.31.1
3 years, 3 months
[libvirt PATCH] meson: Increase stack size limit for sanitizer builds
by Tim Wiederhake
When building with "CC=clang", "-Db_sanitize=address,undefined", and
"-Dbuildtype=debug", the following error occurs:
../src/conf/nwfilter_conf.c:2190:1: error: stack frame size of 10616
bytes in function 'virNWFilterRuleDefFixup' [-Werror,-Wframe-larger-than=]
virNWFilterRuleDefFixup(virNWFilterRuleDef *rule)
^
1 error generated.
Enforcing stack frame only makes sense on normal builds when stack usage
is deterministic.
Signed-off-by: Tim Wiederhake <twiederh(a)redhat.com>
---
meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
index 488490181f..be5a99a069 100644
--- a/meson.build
+++ b/meson.build
@@ -225,7 +225,7 @@ alloc_max = run_command(
)
# sanitizer instrumentation may enlarge stack frames
-stack_frame_size = get_option('b_sanitize') == 'none' ? 4096 : 8192
+stack_frame_size = get_option('b_sanitize') == 'none' ? 4096 : 32768
# array_bounds=2 check triggers false positive on some GCC
# versions when using sanitizers. Seen on Fedora 34 with
--
2.31.1
3 years, 3 months