Am Fr., 26. Juni 2026 um 16:12 Uhr schrieb <[email protected]>:
>
> > So far, only rudimentary checks have been made to ensure that
> > the guest only performs state transitions permitted in
> > virtio-v1.2-csd01 5.14.6.6.1 PCM Command Lifecycle. Add a state
> > variable per audio stream and check all state transitions.
> >
> > Because only permitted state transitions are possible, only one
> > copy of the audio stream parameters is required and these do not
> > need to be initialised with default values.
> >
> > The state variable will also make it easier to restore the audio
> > stream after migration.
> >
> > Signed-off-by: Volker Rümelin <[email protected]>
> > [AM: there were too many conflicts, I did `git checkout --ours -- <.>`
> >      and then reimplemented the patch idea
> > /AM]
> > Signed-off-by: Alexander Mikhalitsyn <[email protected]>
> > Message-ID: <[email protected]>
> >
> > diff --git a/include/hw/audio/virtio-snd.h b/include/hw/audio/virtio-snd.h
> > index 41c63b3f23b..72ef34e0976 100644
> > --- a/include/hw/audio/virtio-snd.h
> > +++ b/include/hw/audio/virtio-snd.h
> > @@ -75,8 +75,6 @@ typedef struct VirtIOSoundPCMStream VirtIOSoundPCMStream;
> >
> >  typedef struct virtio_snd_ctrl_command virtio_snd_ctrl_command;
> >
> > -typedef struct VirtIOSoundPCM VirtIOSoundPCM;
> > -
> >  typedef struct VirtIOSoundPCMBuffer VirtIOSoundPCMBuffer;
> >
> >  /*
> > @@ -121,31 +119,18 @@ struct VirtIOSoundPCMBuffer {
> >      uint8_t data[];
> >  };
> >
> > -struct VirtIOSoundPCM {
> > -    /*
> > -     * PCM parameters are a separate field instead of a 
> > VirtIOSoundPCMStream
> > -     * field, because the operation of PCM control requests is first
> > -     * VIRTIO_SND_R_PCM_SET_PARAMS and then VIRTIO_SND_R_PCM_PREPARE; this
> > -     * means that some times we get parameters without having an allocated
> > -     * stream yet.
> > -     */
> > -    virtio_snd_pcm_set_params *pcm_params;
> > -    VirtIOSoundPCMStream **streams;
> > -};
> > -
> >  struct VirtIOSoundPCMStream {
> >      virtio_snd_pcm_info info;
> >      virtio_snd_pcm_set_params params;
> > +    uint32_t state;
> >      /* channel position values (VIRTIO_SND_CHMAP_XXX) */
> >      uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE];
> >      VirtIOSound *s;
> > -    bool flushing;
> >      audsettings as;
> >      union {
> >          SWVoiceIn *in;
> >          SWVoiceOut *out;
> >      } voice;
> > -    bool active;
> >      uint32_t latency_bytes;
> >      QSIMPLEQ_HEAD(, VirtIOSoundPCMBuffer) queue;
> >  };
> > @@ -212,7 +197,6 @@ struct VirtIOSound {
> >
> >      VirtQueue *queues[VIRTIO_SND_VQ_MAX];
> >      uint64_t features;
> > -    VirtIOSoundPCM pcm;
> >      VirtIOSoundPCMStream *streams;
> >      AudioBackend *audio_be;
> >      VMChangeStateEntry *vmstate;
> > diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c
> > index 300ba13ffeb..68d737478f5 100644
> > --- a/hw/audio/virtio-snd.c
> > +++ b/hw/audio/virtio-snd.c
> > @@ -30,11 +30,30 @@
> >  #define VIRTIO_SOUND_CHMAP_DEFAULT 0
> >  #define VIRTIO_SOUND_HDA_FN_NID 0
> >
> > +#define VSND_PCMSTREAM_STATE_F_PARAMS_SET  0x10000
> > +#define VSND_PCMSTREAM_STATE_F_PREPARED    0x20000
> > +#define VSND_PCMSTREAM_STATE_F_ACTIVE      0x40000
> > +
> > +#define VSND_PCMSTREAM_STATE_UNINITIALIZED 0
> > +#define VSND_PCMSTREAM_STATE_PARAMS_SET    (1 \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PARAMS_SET)
> > +#define VSND_PCMSTREAM_STATE_PREPARED      (2 \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PARAMS_SET \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PREPARED)
> > +#define VSND_PCMSTREAM_STATE_STARTED       (4 \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PARAMS_SET \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PREPARED \
> > +                                           | VSND_PCMSTREAM_STATE_F_ACTIVE)
> > +#define VSND_PCMSTREAM_STATE_STOPPED       (6 \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PARAMS_SET \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PREPARED)
> > +#define VSND_PCMSTREAM_STATE_RELEASED      (7 \
> > +                                           | 
> > VSND_PCMSTREAM_STATE_F_PARAMS_SET)
>

Dear Marc-André,

> Hmm, those 1,2,4,6,7 values matches the spec 5.14.6.6.1 states. Ok, but worth 
> a
> comment imho.

Manos made a point that we can simplify these definitions and instead
of using bitmask,
just have enum like:

+ enum virtio_snd_pcm_state {
+   VIRTIO_SND_PCM_STATE_UNINIT = 0,
+   VIRTIO_SND_PCM_STATE_PARAMS_SET,
+   VIRTIO_SND_PCM_STATE_PREPARED,
+   VIRTIO_SND_PCM_STATE_STARTED,
+   VIRTIO_SND_PCM_STATE_STOPPED,
+   VIRTIO_SND_PCM_STATE_RELEASED,
+ }

(see https://lore.kernel.org/qemu-devel/[email protected]/#t )

WDYT?

>
> > +
> >  static void virtio_snd_pcm_out_cb(void *data, int available);
> >  static void virtio_snd_process_cmdq(VirtIOSound *s);
> >  static void virtio_snd_pcm_flush(VirtIOSoundPCMStream *stream);
> >  static void virtio_snd_pcm_in_cb(void *data, int available);
> > -static void virtio_snd_unrealize(DeviceState *dev);
> >
> >  static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
> >                                    | BIT(VIRTIO_SND_PCM_FMT_U8)
> > @@ -129,7 +148,7 @@ static VirtIOSoundPCMStream 
> > *virtio_snd_pcm_get_stream(VirtIOSound *s,
> >                                                         uint32_t stream_id)
> >  {
> >      return stream_id >= s->snd_conf.streams ? NULL :
> > -        s->pcm.streams[stream_id];
> > +        &s->streams[stream_id];
> >  }
> >
> >  /*
> > @@ -141,8 +160,8 @@ static VirtIOSoundPCMStream 
> > *virtio_snd_pcm_get_stream(VirtIOSound *s,
> >  static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
> >                                                              uint32_t 
> > stream_id)
> >  {
> > -    return stream_id >= s->snd_conf.streams ? NULL
> > -        : &s->pcm.pcm_params[stream_id];
> > +    return stream_id >= s->snd_conf.streams ? NULL :
> > +        &s->streams[stream_id].params;
> >  }
> >
> >  /*
> > @@ -245,11 +264,10 @@ static void virtio_snd_handle_pcm_info(VirtIOSound *s,
> >
> >  /*
> >   * Set the given stream params.
> > - * Called by both virtio_snd_handle_pcm_set_params and during device
> > - * initialization.
> >   * Returns the response status code. (VIRTIO_SND_S_*).
> >   *
> >   * @s: VirtIOSound device
> > + * @stream_id: stream id
> >   * @params: The PCM params as defined in the virtio specification
> >   */
> >  static
> > @@ -257,14 +275,25 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
> >                                     uint32_t stream_id,
> >                                     virtio_snd_pcm_set_params *params)
> >  {
> > +    VirtIOSoundPCMStream *stream;
> >      virtio_snd_pcm_set_params *st_params;
> >
> > -    if (stream_id >= s->snd_conf.streams || s->pcm.pcm_params == NULL) {
> > +    if (stream_id >= s->snd_conf.streams) {
> >          virtio_error(VIRTIO_DEVICE(s), "Streams have not been 
> > initialized.\n");
>
> The error message should be updated.

Thanks, I'll fix this in v4.

>
> >          return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> >      }
> >
> > -    st_params = virtio_snd_pcm_get_params(s, stream_id);
> > +    stream = virtio_snd_pcm_get_stream(s, stream_id);
> > +
> > +    switch (stream->state) {
> > +    case VSND_PCMSTREAM_STATE_UNINITIALIZED:
> > +    case VSND_PCMSTREAM_STATE_PARAMS_SET:
> > +    case VSND_PCMSTREAM_STATE_PREPARED:
> > +    case VSND_PCMSTREAM_STATE_RELEASED:
> > +        break;
> > +    default:
>
> Keep a virtio_error()?

Sure, will do in v4.

>
> > +        return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> > +    }
> >
> >      if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
> >          error_report("Number of channels is not supported.");
> > @@ -281,6 +310,8 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
> >          return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
> >      }
> >
> > +    st_params = virtio_snd_pcm_get_params(s, stream_id);
> > +
> >      st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
> >      st_params->period_bytes = le32_to_cpu(params->period_bytes);
> >      st_params->features = le32_to_cpu(params->features);
> > @@ -289,6 +320,13 @@ uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
> >      st_params->format = params->format;
> >      st_params->rate = params->rate;
> >
> > +    if (stream->state & VSND_PCMSTREAM_STATE_F_PREPARED) {
> > +        /* implicit VIRTIO_SND_R_PCM_RELEASE */
> > +        virtio_snd_pcm_flush(stream);
> > +    }
>
> I'd leave a comment to explain we keep the SWVoice opened.

I decided to add virtio_snd_pcm_close(stream) call to
virtio_snd_set_pcm_params().

>
> > +
> > +    stream->state = VSND_PCMSTREAM_STATE_PARAMS_SET;
> > +
> >      return cpu_to_le32(VIRTIO_SND_S_OK);
> >  }
> >
> > @@ -398,15 +436,12 @@ static void 
> > virtio_snd_get_qemu_audsettings(audsettings *as,
> >   */
> >  static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
> >  {
> > -    if (stream) {
> > -        virtio_snd_pcm_flush(stream);
> > -        if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> > -            audio_be_close_out(stream->s->audio_be, stream->voice.out);
> > -            stream->voice.out = NULL;
> > -        } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
> > -            audio_be_close_in(stream->s->audio_be, stream->voice.in);
> > -            stream->voice.in = NULL;
> > -        }
> > +    if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> > +        audio_be_close_out(stream->s->audio_be, stream->voice.out);
> > +        stream->voice.out = NULL;
> > +    } else if (stream->info.direction == VIRTIO_SND_D_INPUT) {
> > +        audio_be_close_in(stream->s->audio_be, stream->voice.in);
> > +        stream->voice.in = NULL;
> >      }
> >  }
> >
> > @@ -423,32 +458,23 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound 
> > *s, uint32_t stream_id)
> >      virtio_snd_pcm_set_params *params;
> >      VirtIOSoundPCMStream *stream;
> >
> > -    if (s->pcm.streams == NULL ||
> > -        s->pcm.pcm_params == NULL ||
> > -        stream_id >= s->snd_conf.streams) {
> > +    stream = virtio_snd_pcm_get_stream(s, stream_id);
> > +    if (!stream) {
> >          return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> >      }
> >
> > -    params = virtio_snd_pcm_get_params(s, stream_id);
> > -    if (params == NULL) {
> > +    switch (stream->state) {
> > +    case VSND_PCMSTREAM_STATE_PARAMS_SET:
> > +    case VSND_PCMSTREAM_STATE_PREPARED:
> > +    case VSND_PCMSTREAM_STATE_RELEASED:
> > +        break;
> > +    default:
> >          return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> >      }
> >
> > -    stream = virtio_snd_pcm_get_stream(s, stream_id);
> > -    if (stream == NULL) {
> > -        stream = &s->streams[stream_id];
> > -        stream->active = false;
> > -        stream->latency_bytes = 0;
> > -
> > -        /*
> > -         * stream_id >= s->snd_conf.streams was checked before so this is
> > -         * in-bounds
> > -         */
> > -        s->pcm.streams[stream_id] = stream;
> > -    }
> > +    params = virtio_snd_pcm_get_params(s, stream_id);
> >
> >      virtio_snd_get_qemu_audsettings(&as, params);
> > -    stream->params = *params;
> >
> >      stream->positions[0] = VIRTIO_SND_CHMAP_FL;
> >      stream->positions[1] = VIRTIO_SND_CHMAP_FR;
> > @@ -472,6 +498,8 @@ static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, 
> > uint32_t stream_id)
> >          audio_be_set_volume_in_lr(s->audio_be, stream->voice.in, 0, 255, 
> > 255);
> >      }
> >
> > +    stream->state = VSND_PCMSTREAM_STATE_PREPARED;
> > +
> >      return cpu_to_le32(VIRTIO_SND_S_OK);
> >  }
> >
> > @@ -542,12 +570,28 @@ static uint32_t virtio_snd_pcm_start_stop(VirtIOSound 
> > *s,
> >      }
> >
> >      if (start) {
> > +        switch (stream->state) {
> > +        case VSND_PCMSTREAM_STATE_PREPARED:
> > +        case VSND_PCMSTREAM_STATE_STOPPED:
> > +            break;
> > +        default:
> > +            return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> > +        }
> > +
> >          trace_virtio_snd_handle_pcm_start(stream_id);
> > +        stream->state = VSND_PCMSTREAM_STATE_STARTED;
> >      } else {
> > +        switch (stream->state) {
> > +        case VSND_PCMSTREAM_STATE_STARTED:
> > +            break;
> > +        default:
> > +            return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> > +        }
> > +
> >          trace_virtio_snd_handle_pcm_stop(stream_id);
> > +        stream->state = VSND_PCMSTREAM_STATE_STOPPED;
> >      }
> >
> > -    stream->active = start;
> >      if (stream->info.direction == VIRTIO_SND_D_OUTPUT) {
> >          audio_be_set_active_out(s->audio_be, stream->voice.out, start);
> >      } else {
> > @@ -641,6 +685,15 @@ static void virtio_snd_handle_pcm_release(VirtIOSound 
> > *s,
> >          return;
> >      }
> >
> > +    switch (stream->state) {
> > +    case VSND_PCMSTREAM_STATE_PREPARED:
> > +    case VSND_PCMSTREAM_STATE_STOPPED:
> > +        break;
> > +    default:
> > +        cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
> > +        return;
> > +    }
> > +
> >      if (virtio_snd_pcm_get_io_msgs_count(stream)) {
> >          /*
> >           * virtio-v1.2-csd01, 5.14.6.6.5.1,
> > @@ -655,6 +708,8 @@ static void virtio_snd_handle_pcm_release(VirtIOSound 
> > *s,
> >          virtio_snd_pcm_flush(stream);
> >      }
>
> Same here.

I decided to add virtio_snd_pcm_close(stream) call to
virtio_snd_handle_pcm_release().

Kind regards,
Alex

>
> --
> Marc-André Lureau <[email protected]>
>

Reply via email to