cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

virtio_ctl_msg.c (8110B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * virtio-snd: Virtio sound device
      4 * Copyright (C) 2021 OpenSynergy GmbH
      5 */
      6#include <linux/moduleparam.h>
      7#include <linux/virtio_config.h>
      8
      9#include "virtio_card.h"
     10
     11/**
     12 * struct virtio_snd_msg - Control message.
     13 * @sg_request: Scattergather list containing a device request (header).
     14 * @sg_response: Scattergather list containing a device response (status).
     15 * @list: Pending message list entry.
     16 * @notify: Request completed notification.
     17 * @ref_count: Reference count used to manage a message lifetime.
     18 */
     19struct virtio_snd_msg {
     20	struct scatterlist sg_request;
     21	struct scatterlist sg_response;
     22	struct list_head list;
     23	struct completion notify;
     24	refcount_t ref_count;
     25};
     26
     27/**
     28 * virtsnd_ctl_msg_ref() - Increment reference counter for the message.
     29 * @msg: Control message.
     30 *
     31 * Context: Any context.
     32 */
     33void virtsnd_ctl_msg_ref(struct virtio_snd_msg *msg)
     34{
     35	refcount_inc(&msg->ref_count);
     36}
     37
     38/**
     39 * virtsnd_ctl_msg_unref() - Decrement reference counter for the message.
     40 * @msg: Control message.
     41 *
     42 * The message will be freed when the ref_count value is 0.
     43 *
     44 * Context: Any context.
     45 */
     46void virtsnd_ctl_msg_unref(struct virtio_snd_msg *msg)
     47{
     48	if (refcount_dec_and_test(&msg->ref_count))
     49		kfree(msg);
     50}
     51
     52/**
     53 * virtsnd_ctl_msg_request() - Get a pointer to the request header.
     54 * @msg: Control message.
     55 *
     56 * Context: Any context.
     57 */
     58void *virtsnd_ctl_msg_request(struct virtio_snd_msg *msg)
     59{
     60	return sg_virt(&msg->sg_request);
     61}
     62
     63/**
     64 * virtsnd_ctl_msg_response() - Get a pointer to the response header.
     65 * @msg: Control message.
     66 *
     67 * Context: Any context.
     68 */
     69void *virtsnd_ctl_msg_response(struct virtio_snd_msg *msg)
     70{
     71	return sg_virt(&msg->sg_response);
     72}
     73
     74/**
     75 * virtsnd_ctl_msg_alloc() - Allocate and initialize a control message.
     76 * @request_size: Size of request header.
     77 * @response_size: Size of response header.
     78 * @gfp: Kernel flags for memory allocation.
     79 *
     80 * The message will be automatically freed when the ref_count value is 0.
     81 *
     82 * Context: Any context. May sleep if @gfp flags permit.
     83 * Return: Allocated message on success, NULL on failure.
     84 */
     85struct virtio_snd_msg *virtsnd_ctl_msg_alloc(size_t request_size,
     86					     size_t response_size, gfp_t gfp)
     87{
     88	struct virtio_snd_msg *msg;
     89
     90	if (!request_size || !response_size)
     91		return NULL;
     92
     93	msg = kzalloc(sizeof(*msg) + request_size + response_size, gfp);
     94	if (!msg)
     95		return NULL;
     96
     97	sg_init_one(&msg->sg_request, (u8 *)msg + sizeof(*msg), request_size);
     98	sg_init_one(&msg->sg_response, (u8 *)msg + sizeof(*msg) + request_size,
     99		    response_size);
    100
    101	INIT_LIST_HEAD(&msg->list);
    102	init_completion(&msg->notify);
    103	/* This reference is dropped in virtsnd_ctl_msg_complete(). */
    104	refcount_set(&msg->ref_count, 1);
    105
    106	return msg;
    107}
    108
    109/**
    110 * virtsnd_ctl_msg_send() - Send a control message.
    111 * @snd: VirtIO sound device.
    112 * @msg: Control message.
    113 * @out_sgs: Additional sg-list to attach to the request header (may be NULL).
    114 * @in_sgs: Additional sg-list to attach to the response header (may be NULL).
    115 * @nowait: Flag indicating whether to wait for completion.
    116 *
    117 * Context: Any context. Takes and releases the control queue spinlock.
    118 *          May sleep if @nowait is false.
    119 * Return: 0 on success, -errno on failure.
    120 */
    121int virtsnd_ctl_msg_send(struct virtio_snd *snd, struct virtio_snd_msg *msg,
    122			 struct scatterlist *out_sgs,
    123			 struct scatterlist *in_sgs, bool nowait)
    124{
    125	struct virtio_device *vdev = snd->vdev;
    126	struct virtio_snd_queue *queue = virtsnd_control_queue(snd);
    127	unsigned int js = msecs_to_jiffies(virtsnd_msg_timeout_ms);
    128	struct virtio_snd_hdr *request = virtsnd_ctl_msg_request(msg);
    129	struct virtio_snd_hdr *response = virtsnd_ctl_msg_response(msg);
    130	unsigned int nouts = 0;
    131	unsigned int nins = 0;
    132	struct scatterlist *psgs[4];
    133	bool notify = false;
    134	unsigned long flags;
    135	int rc;
    136
    137	virtsnd_ctl_msg_ref(msg);
    138
    139	/* Set the default status in case the message was canceled. */
    140	response->code = cpu_to_le32(VIRTIO_SND_S_IO_ERR);
    141
    142	psgs[nouts++] = &msg->sg_request;
    143	if (out_sgs)
    144		psgs[nouts++] = out_sgs;
    145
    146	psgs[nouts + nins++] = &msg->sg_response;
    147	if (in_sgs)
    148		psgs[nouts + nins++] = in_sgs;
    149
    150	spin_lock_irqsave(&queue->lock, flags);
    151	rc = virtqueue_add_sgs(queue->vqueue, psgs, nouts, nins, msg,
    152			       GFP_ATOMIC);
    153	if (!rc) {
    154		notify = virtqueue_kick_prepare(queue->vqueue);
    155
    156		list_add_tail(&msg->list, &snd->ctl_msgs);
    157	}
    158	spin_unlock_irqrestore(&queue->lock, flags);
    159
    160	if (rc) {
    161		dev_err(&vdev->dev, "failed to send control message (0x%08x)\n",
    162			le32_to_cpu(request->code));
    163
    164		/*
    165		 * Since in this case virtsnd_ctl_msg_complete() will not be
    166		 * called, it is necessary to decrement the reference count.
    167		 */
    168		virtsnd_ctl_msg_unref(msg);
    169
    170		goto on_exit;
    171	}
    172
    173	if (notify)
    174		virtqueue_notify(queue->vqueue);
    175
    176	if (nowait)
    177		goto on_exit;
    178
    179	rc = wait_for_completion_interruptible_timeout(&msg->notify, js);
    180	if (rc <= 0) {
    181		if (!rc) {
    182			dev_err(&vdev->dev,
    183				"control message (0x%08x) timeout\n",
    184				le32_to_cpu(request->code));
    185			rc = -ETIMEDOUT;
    186		}
    187
    188		goto on_exit;
    189	}
    190
    191	switch (le32_to_cpu(response->code)) {
    192	case VIRTIO_SND_S_OK:
    193		rc = 0;
    194		break;
    195	case VIRTIO_SND_S_NOT_SUPP:
    196		rc = -EOPNOTSUPP;
    197		break;
    198	case VIRTIO_SND_S_IO_ERR:
    199		rc = -EIO;
    200		break;
    201	default:
    202		rc = -EINVAL;
    203		break;
    204	}
    205
    206on_exit:
    207	virtsnd_ctl_msg_unref(msg);
    208
    209	return rc;
    210}
    211
    212/**
    213 * virtsnd_ctl_msg_complete() - Complete a control message.
    214 * @msg: Control message.
    215 *
    216 * Context: Any context. Expects the control queue spinlock to be held by
    217 *          caller.
    218 */
    219void virtsnd_ctl_msg_complete(struct virtio_snd_msg *msg)
    220{
    221	list_del(&msg->list);
    222	complete(&msg->notify);
    223
    224	virtsnd_ctl_msg_unref(msg);
    225}
    226
    227/**
    228 * virtsnd_ctl_msg_cancel_all() - Cancel all pending control messages.
    229 * @snd: VirtIO sound device.
    230 *
    231 * Context: Any context.
    232 */
    233void virtsnd_ctl_msg_cancel_all(struct virtio_snd *snd)
    234{
    235	struct virtio_snd_queue *queue = virtsnd_control_queue(snd);
    236	unsigned long flags;
    237
    238	spin_lock_irqsave(&queue->lock, flags);
    239	while (!list_empty(&snd->ctl_msgs)) {
    240		struct virtio_snd_msg *msg =
    241			list_first_entry(&snd->ctl_msgs, struct virtio_snd_msg,
    242					 list);
    243
    244		virtsnd_ctl_msg_complete(msg);
    245	}
    246	spin_unlock_irqrestore(&queue->lock, flags);
    247}
    248
    249/**
    250 * virtsnd_ctl_query_info() - Query the item configuration from the device.
    251 * @snd: VirtIO sound device.
    252 * @command: Control request code (VIRTIO_SND_R_XXX_INFO).
    253 * @start_id: Item start identifier.
    254 * @count: Item count to query.
    255 * @size: Item information size in bytes.
    256 * @info: Buffer for storing item information.
    257 *
    258 * Context: Any context that permits to sleep.
    259 * Return: 0 on success, -errno on failure.
    260 */
    261int virtsnd_ctl_query_info(struct virtio_snd *snd, int command, int start_id,
    262			   int count, size_t size, void *info)
    263{
    264	struct virtio_snd_msg *msg;
    265	struct virtio_snd_query_info *query;
    266	struct scatterlist sg;
    267
    268	msg = virtsnd_ctl_msg_alloc(sizeof(*query),
    269				    sizeof(struct virtio_snd_hdr), GFP_KERNEL);
    270	if (!msg)
    271		return -ENOMEM;
    272
    273	query = virtsnd_ctl_msg_request(msg);
    274	query->hdr.code = cpu_to_le32(command);
    275	query->start_id = cpu_to_le32(start_id);
    276	query->count = cpu_to_le32(count);
    277	query->size = cpu_to_le32(size);
    278
    279	sg_init_one(&sg, info, count * size);
    280
    281	return virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false);
    282}
    283
    284/**
    285 * virtsnd_ctl_notify_cb() - Process all completed control messages.
    286 * @vqueue: Underlying control virtqueue.
    287 *
    288 * This callback function is called upon a vring interrupt request from the
    289 * device.
    290 *
    291 * Context: Interrupt context. Takes and releases the control queue spinlock.
    292 */
    293void virtsnd_ctl_notify_cb(struct virtqueue *vqueue)
    294{
    295	struct virtio_snd *snd = vqueue->vdev->priv;
    296	struct virtio_snd_queue *queue = virtsnd_control_queue(snd);
    297	struct virtio_snd_msg *msg;
    298	u32 length;
    299	unsigned long flags;
    300
    301	spin_lock_irqsave(&queue->lock, flags);
    302	do {
    303		virtqueue_disable_cb(vqueue);
    304		while ((msg = virtqueue_get_buf(vqueue, &length)))
    305			virtsnd_ctl_msg_complete(msg);
    306		if (unlikely(virtqueue_is_broken(vqueue)))
    307			break;
    308	} while (!virtqueue_enable_cb(vqueue));
    309	spin_unlock_irqrestore(&queue->lock, flags);
    310}