qemu-kvm: Make PC speaker emulation aware of in-kernel PIT
PATCH v2 1/1 qemu-kvm: Make PC speaker emulation aware of in-kernel PIT
When using the in-kernel PIT the speaker emulation has to synchronize the PIT state with KVM. Enhance the existing speaker sound device and allow it to take over port 0x61 by using KVM_CREATE_PIT2 where available. This unbreaks -soundhw pcspk in KVM mode.
Changes in v2:
- rebased over qemu-kvm and KVM_CREATE_PIT2
- refactored hooks in pcspk
---
hw/pcspk.c | 41 ++++++++++++++++++++++++++++++++++++++++ kvm/kernel/include/linux/kvm.h | 10 ++++++++++ kvm/libkvm/libkvm-x86.c | 26 ++++++++++++++++++------- 3 files changed, 69 insertions(+), 8 deletions(-)
hw/pcspk.c +41 -0
@@ -27,6 +27,8 @@#include "isa.h" #include "audio/audio.h" #include "qemu-timer.h" +#include "i8254.h" +#include "qemu-kvm.h" #define PCSPK_BUF_LEN 1792#define PCSPK_SAMPLE_RATE 32000
@@ -48,6 +50,37 @@ typedef struct {static const char *s_spk ="pcspk" ;static PCSpkState pcspk_state; +#ifdef USE_KVM_PIT +static void kvm_get_pit_ch2 (PITState *pit) +{ +struct kvm_pit_state pit_state; + +if (qemu_kvm_pit_in_kernel ()) { +kvm_get_pit (kvm_context, &pit_state); + pit->channels[2 ].mode = pit_state.channels[2 ].mode; + pit->channels[2 ].count = pit_state.channels[2 ].count; + pit->channels[2 ].count_load_time = pit_state.channels[2 ].count_load_time; + pit->channels[2 ].gate = pit_state.channels[2 ].gate; + } +} + +static void kvm_set_pit_ch2 (PITState *pit) +{ +struct kvm_pit_state pit_state; + +if (qemu_kvm_pit_in_kernel ()) { + pit_state.channels[2 ].mode = pit->channels[2 ].mode; + pit_state.channels[2 ].count = pit->channels[2 ].count; + pit_state.channels[2 ].count_load_time = pit->channels[2 ].count_load_time; + pit_state.channels[2 ].gate = pit->channels[2 ].gate; +kvm_set_pit (kvm_context, &pit_state); + } +} +#else +static inline void kvm_get_pit_ch2 (PITState *pit) { } +static inline void kvm_set_pit_ch2 (PITState *pit) { } +#endif +static inline void generate_samples (PCSpkState *s) {unsigned int i;
@@ -72,6 +105,8 @@ static void pcspk_callback(void *opaque, int free)PCSpkState *s = opaque;unsigned int n; +kvm_get_pit_ch2 (s->pit); +if (pit_get_mode (s->pit,2 ) !=3 )return ;
@@ -121,6 +156,8 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)PCSpkState *s = opaque;int out; +kvm_get_pit_ch2 (s->pit); + s->dummy_refresh_clock ^= (1 <<4 ); out =pit_get_out (s->pit,2 ,qemu_get_clock (vm_clock)) <<5 ;
@@ -132,6 +169,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)PCSpkState *s = opaque;const int gate = val &1 ; +kvm_get_pit_ch2 (s->pit); + s->data_on = (val >>1 ) &1 ;pit_set_gate (s->pit,2 , gate);if (s->voice) {
@@ -139,6 +178,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->play_pos =0 ;AUD_set_active_out (s->voice, gate & s->data_on); } + +kvm_set_pit_ch2 (s->pit); }void pcspk_init (PITState *pit)
kvm/kernel/include/linux/kvm.h +10 -0
@@ -110,6 +110,14 @@ struct kvm_irqchip { } chip; }; +/* for KVM_CREATE_PIT2 */ +struct kvm_pit_config { +__u32 flags; +__u32 pad; +}; + +#define KVM_PIT_SPEAKER_DUMMY 1 +#define KVM_EXIT_UNKNOWN 0#define KVM_EXIT_EXCEPTION 1#define KVM_EXIT_IO 2
@@ -455,6 +463,7 @@ struct kvm_trace_rec {#define KVM_CAP_ASSIGN_DEV_IRQ 29/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#define KVM_CAP_PIT2 31#ifdef KVM_CAP_IRQ_ROUTING
@@ -538,6 +547,7 @@ struct kvm_irq_routing {#define KVM_ASSIGN_SET_MSIX_ENTRY \ _IOW(KVMIO, 0x74, struct kvm_assigned_msix_entry)#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) +#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x76, struct kvm_pit_config)/* * ioctls for vcpu fds
kvm/libkvm/libkvm-x86.c +18 -8
@@ -59,16 +59,26 @@ static int kvm_create_pit(kvm_context_t kvm) kvm->pit_in_kernel =0 ;if (!kvm->no_pit_creation) { - r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); -if (r >0 ) { +#ifdef KVM_CAP_PIT2 +struct kvm_pit_config config = { .flags =0 }; + + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT2 ); +if (r >0 ) + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT2 , &config); + else +#endif + { + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); +if (r <=0 ) +return 0 ; + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT ); -if (r >=0 ) - kvm->pit_in_kernel =1 ; -else { -fprintf (stderr,"Create kernel PIC irqchip failed\n" ); -return r; - } } +if (r <0 ) { +fprintf (stderr,"Create kernel PIC irqchip failed\n" ); +return r; + } + kvm->pit_in_kernel =1 ; }#endif return 0 ;
PATCH v3 1/1 qemu-kvm: Make PC speaker emulation aware of in-kernel PIT
When using the in-kernel PIT the speaker emulation has to synchronize the PIT state with KVM. Enhance the existing speaker sound device and allow it to take over port 0x61 by using KVM_CREATE_PIT2 where available. This unbreaks -soundhw pcspk in KVM mode.
Changes in v3:
- re-added incorrectly dropped kvm_enabled checks
Changes in v2:
- rebased over qemu-kvm and KVM_CREATE_PIT2
- refactored hooks in pcspk
---
hw/pcspk.c | 41 ++++++++++++++++++++++++++++++++++++++++ kvm/kernel/include/linux/kvm.h | 10 ++++++++++ kvm/libkvm/libkvm-x86.c | 26 ++++++++++++++++++------- 3 files changed, 69 insertions(+), 8 deletions(-)
hw/pcspk.c +41 -0
@@ -27,6 +27,8 @@#include "isa.h" #include "audio/audio.h" #include "qemu-timer.h" +#include "i8254.h" +#include "qemu-kvm.h" #define PCSPK_BUF_LEN 1792#define PCSPK_SAMPLE_RATE 32000
@@ -48,6 +50,37 @@ typedef struct {static const char *s_spk ="pcspk" ;static PCSpkState pcspk_state; +#ifdef USE_KVM_PIT +static void kvm_get_pit_ch2 (PITState *pit) +{ +struct kvm_pit_state pit_state; + +if (kvm_enabled () &&qemu_kvm_pit_in_kernel ()) { +kvm_get_pit (kvm_context, &pit_state); + pit->channels[2 ].mode = pit_state.channels[2 ].mode; + pit->channels[2 ].count = pit_state.channels[2 ].count; + pit->channels[2 ].count_load_time = pit_state.channels[2 ].count_load_time; + pit->channels[2 ].gate = pit_state.channels[2 ].gate; + } +} + +static void kvm_set_pit_ch2 (PITState *pit) +{ +struct kvm_pit_state pit_state; + +if (kvm_enabled () &&qemu_kvm_pit_in_kernel ()) { + pit_state.channels[2 ].mode = pit->channels[2 ].mode; + pit_state.channels[2 ].count = pit->channels[2 ].count; + pit_state.channels[2 ].count_load_time = pit->channels[2 ].count_load_time; + pit_state.channels[2 ].gate = pit->channels[2 ].gate; +kvm_set_pit (kvm_context, &pit_state); + } +} +#else +static inline void kvm_get_pit_ch2 (PITState *pit) { } +static inline void kvm_set_pit_ch2 (PITState *pit) { } +#endif +static inline void generate_samples (PCSpkState *s) {unsigned int i;
@@ -72,6 +105,8 @@ static void pcspk_callback(void *opaque, int free)PCSpkState *s = opaque;unsigned int n; +kvm_get_pit_ch2 (s->pit); +if (pit_get_mode (s->pit,2 ) !=3 )return ;
@@ -121,6 +156,8 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)PCSpkState *s = opaque;int out; +kvm_get_pit_ch2 (s->pit); + s->dummy_refresh_clock ^= (1 <<4 ); out =pit_get_out (s->pit,2 ,qemu_get_clock (vm_clock)) <<5 ;
@@ -132,6 +169,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)PCSpkState *s = opaque;const int gate = val &1 ; +kvm_get_pit_ch2 (s->pit); + s->data_on = (val >>1 ) &1 ;pit_set_gate (s->pit,2 , gate);if (s->voice) {
@@ -139,6 +178,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->play_pos =0 ;AUD_set_active_out (s->voice, gate & s->data_on); } + +kvm_set_pit_ch2 (s->pit); }void pcspk_init (PITState *pit)
kvm/kernel/include/linux/kvm.h +10 -0
@@ -110,6 +110,14 @@ struct kvm_irqchip { } chip; }; +/* for KVM_CREATE_PIT2 */ +struct kvm_pit_config { +__u32 flags; +__u32 pad; +}; + +#define KVM_PIT_SPEAKER_DUMMY 1 +#define KVM_EXIT_UNKNOWN 0#define KVM_EXIT_EXCEPTION 1#define KVM_EXIT_IO 2
@@ -455,6 +463,7 @@ struct kvm_trace_rec {#define KVM_CAP_ASSIGN_DEV_IRQ 29/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#define KVM_CAP_PIT2 31#ifdef KVM_CAP_IRQ_ROUTING
@@ -538,6 +547,7 @@ struct kvm_irq_routing {#define KVM_ASSIGN_SET_MSIX_ENTRY \ _IOW(KVMIO, 0x74, struct kvm_assigned_msix_entry)#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) +#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x76, struct kvm_pit_config)/* * ioctls for vcpu fds
kvm/libkvm/libkvm-x86.c +18 -8
@@ -59,16 +59,26 @@ static int kvm_create_pit(kvm_context_t kvm) kvm->pit_in_kernel =0 ;if (!kvm->no_pit_creation) { - r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); -if (r >0 ) { +#ifdef KVM_CAP_PIT2 +struct kvm_pit_config config = { .flags =0 }; + + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT2 ); +if (r >0 ) + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT2 , &config); + else +#endif + { + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); +if (r <=0 ) +return 0 ; + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT ); -if (r >=0 ) - kvm->pit_in_kernel =1 ; -else { -fprintf (stderr,"Create kernel PIC irqchip failed\n" ); -return r; - } } +if (r <0 ) { +fprintf (stderr,"Create kernel PIC irqchip failed\n" ); +return r; + } + kvm->pit_in_kernel =1 ; }#endif return 0 ;
PATCH v4 1/1 qemu-kvm: Make PC speaker emulation aware of in-kernel PIT
When using the in-kernel PIT the speaker emulation has to synchronize the PIT state with KVM. Enhance the existing speaker sound device and allow it to take over port 0x61 by using KVM_CREATE_PIT2 where available. This unbreaks -soundhw pcspk in KVM mode.
Changes in v4:
- preserve full PIT state across read-modify-write
- update kvm.h
Changes in v3:
- re-added incorrectly dropped kvm_enabled checks
Changes in v2:
- rebased over qemu-kvm and KVM_CREATE_PIT2
- refactored hooks in pcspk
---
hw/pcspk.c | 48 ++++++++++++++++++++++++++++++++++++++++ kvm/kernel/include/linux/kvm.h | 10 ++++++++ kvm/libkvm/libkvm-x86.c | 26 +++++++++++++++------- 3 files changed, 76 insertions(+), 8 deletions(-)
hw/pcspk.c +48 -0
@@ -27,6 +27,8 @@#include "isa.h" #include "audio/audio.h" #include "qemu-timer.h" +#include "i8254.h" +#include "qemu-kvm.h" #define PCSPK_BUF_LEN 1792#define PCSPK_SAMPLE_RATE 32000
@@ -48,6 +50,43 @@ typedef struct {static const char *s_spk ="pcspk" ;static PCSpkState pcspk_state; +#ifdef USE_KVM_PIT +static void kvm_get_pit_ch2 (PITState *pit, +struct kvm_pit_state *inkernel_state) +{ +struct kvm_pit_state pit_state; + +if (kvm_enabled () &&qemu_kvm_pit_in_kernel ()) { +kvm_get_pit (kvm_context, &pit_state); + pit->channels[2 ].mode = pit_state.channels[2 ].mode; + pit->channels[2 ].count = pit_state.channels[2 ].count; + pit->channels[2 ].count_load_time = pit_state.channels[2 ].count_load_time; + pit->channels[2 ].gate = pit_state.channels[2 ].gate; +if (inkernel_state) { +memcpy (inkernel_state, &pit_state,sizeof (*inkernel_state)); + } + } +} + +static void kvm_set_pit_ch2 (PITState *pit, +struct kvm_pit_state *inkernel_state) +{ +if (kvm_enabled () &&qemu_kvm_pit_in_kernel ()) { + inkernel_state->channels[2 ].mode = pit->channels[2 ].mode; + inkernel_state->channels[2 ].count = pit->channels[2 ].count; + inkernel_state->channels[2 ].count_load_time = + pit->channels[2 ].count_load_time; + inkernel_state->channels[2 ].gate = pit->channels[2 ].gate; +kvm_set_pit (kvm_context, inkernel_state); + } +} +#else +static inline void kvm_get_pit_ch2 (PITState *pit, +kvm_pit_state *inkernel_state) { } +static inline void kvm_set_pit_ch2 (PITState *pit, +kvm_pit_state *inkernel_state) { } +#endif +static inline void generate_samples (PCSpkState *s) {unsigned int i;
@@ -72,6 +111,8 @@ static void pcspk_callback(void *opaque, int free)PCSpkState *s = opaque;unsigned int n; +kvm_get_pit_ch2 (s->pit,NULL ); +if (pit_get_mode (s->pit,2 ) !=3 )return ;
@@ -121,6 +162,8 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)PCSpkState *s = opaque;int out; +kvm_get_pit_ch2 (s->pit,NULL ); + s->dummy_refresh_clock ^= (1 <<4 ); out =pit_get_out (s->pit,2 ,qemu_get_clock (vm_clock)) <<5 ;
@@ -129,9 +172,12 @@ static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)static void pcspk_ioport_write (void *opaque,uint32_t addr,uint32_t val) { +struct kvm_pit_state inkernel_state;PCSpkState *s = opaque;const int gate = val &1 ; +kvm_get_pit_ch2 (s->pit, &inkernel_state); + s->data_on = (val >>1 ) &1 ;pit_set_gate (s->pit,2 , gate);if (s->voice) {
@@ -139,6 +185,8 @@ static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->play_pos =0 ;AUD_set_active_out (s->voice, gate & s->data_on); } + +kvm_set_pit_ch2 (s->pit, &inkernel_state); }void pcspk_init (PITState *pit)
kvm/kernel/include/linux/kvm.h +10 -0
@@ -110,6 +110,14 @@ struct kvm_irqchip { } chip; }; +/* for KVM_CREATE_PIT2 */ +struct kvm_pit_config { +__u32 flags; +__u32 pad[15 ]; +}; + +#define KVM_PIT_SPEAKER_DUMMY 1 +#define KVM_EXIT_UNKNOWN 0#define KVM_EXIT_EXCEPTION 1#define KVM_EXIT_IO 2
@@ -455,6 +463,7 @@ struct kvm_trace_rec {#define KVM_CAP_ASSIGN_DEV_IRQ 29/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30 +#define KVM_CAP_PIT2 31#ifdef KVM_CAP_IRQ_ROUTING
@@ -538,6 +547,7 @@ struct kvm_irq_routing {#define KVM_ASSIGN_SET_MSIX_ENTRY \ _IOW(KVMIO, 0x74, struct kvm_assigned_msix_entry)#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq) +#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x76, struct kvm_pit_config)/* * ioctls for vcpu fds
kvm/libkvm/libkvm-x86.c +18 -8
@@ -59,16 +59,26 @@ static int kvm_create_pit(kvm_context_t kvm) kvm->pit_in_kernel =0 ;if (!kvm->no_pit_creation) { - r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); -if (r >0 ) { +#ifdef KVM_CAP_PIT2 +struct kvm_pit_config config = { .flags =0 }; + + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT2 ); +if (r >0 ) + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT2 , &config); + else +#endif + { + r =ioctl (kvm->fd,KVM_CHECK_EXTENSION ,KVM_CAP_PIT ); +if (r <=0 ) +return 0 ; + r =ioctl (kvm->vm_fd,KVM_CREATE_PIT ); -if (r >=0 ) - kvm->pit_in_kernel =1 ; -else { -fprintf (stderr,"Create kernel PIC irqchip failed\n" ); -return r; - } } +if (r <0 ) { +fprintf (stderr,"Create kernel PIC irqchip failed\n" ); +return r; + } + kvm->pit_in_kernel =1 ; }#endif return 0 ;
Quoted 9 lines · Jan Kiszka
When using the in-kernel PIT the speaker emulation has to synchronize the PIT state with KVM. Enhance the existing speaker sound device and allow it to take over port
0x61by usingKVM_CREATE_PIT2where available. This unbreaks -soundhw pcspk in KVM mode.Changes in v4:
- preserve full PIT state across read-modify-write
- updatekvm.h
Quoted 8 lines
Changes in v3:
- re-added incorrectly droppedkvm_enabledchecksChanges in v2:
- rebased over qemu-kvm andKVM_CREATE_PIT2
- refactored hooks in pcspkSigned-off-by Jan Kiszka <[email protected]>
Jan,
You always attempt to use KVM_CREATE_PIT2, so say on migration if the destination does not support the new ioctl you fallback to in-kernel dummy naturally. Seems the right thing to do.
Would be nice to avoid sprinkling KVM details inside hw/pcspk.c though but that is another problem.
Looks good (and v3 kernel patch).
Quoted 19 lines · Marcelo Tosatti
Quoted 9 lines · Jan Kiszka
When using the in-kernel PIT the speaker emulation has to synchronize the PIT state with KVM. Enhance the existing speaker sound device and allow it to take over port
0x61by usingKVM_CREATE_PIT2where available. This unbreaks -soundhw pcspk in KVM mode.Changes in v4:
- preserve full PIT state across read-modify-write
- updatekvm.hQuoted 8 lines
Changes in v3:
- re-added incorrectly droppedkvm_enabledchecksChanges in v2:
- rebased over qemu-kvm andKVM_CREATE_PIT2
- refactored hooks in pcspkSigned-off-by Jan Kiszka <[email protected]>
Quoted 8 lines
Jan,
You always attempt to use
KVM_CREATE_PIT2, so say on migration if the destination does not support the new ioctl you fallback to in-kernel dummy naturally. Seems the right thing to do.Would be nice to avoid sprinkling KVM details inside
hw/pcspk.cthough but that is another problem.
Does this remark prevent merging the patch ATM? I do not see another way how to make the pcspk aware of the in-kernel PIT, given that the speaker is naturally coupled to PIT channel 2.
Quoted 3 lines
Looks good (and v3 kernel patch).
Jan
Quoted 6 lines · Jan Kiszka
Quoted 3 lines
Would be nice to avoid sprinkling KVM details inside
hw/pcspk.cthough but that is another problem.Does this remark prevent merging the patch ATM?
It doesn't, so I applied the patch. Thanks.
Signature · 1 line
error compiling committee.c: too many arguments to function