arch/um/drivers/virtio_pcidev.c

Source file repositories/reference/linux-study-clean/arch/um/drivers/virtio_pcidev.c

File Facts

System
Linux kernel
Corpus path
arch/um/drivers/virtio_pcidev.c
Extension
.c
Size
14666 bytes
Lines
635
Domain
Architecture Layer
Bucket
arch/um
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct virtio_pcidev_message_buffer {
	struct virtio_pcidev_msg hdr;
	u8 data[8];
};

struct virtio_pcidev_device {
	struct um_pci_device pdev;
	struct virtio_device *vdev;

	struct virtqueue *cmd_vq, *irq_vq;

#define VIRTIO_PCIDEV_WRITE_BUFS	20
	struct virtio_pcidev_message_buffer bufs[VIRTIO_PCIDEV_WRITE_BUFS + 1];
	void *extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS + 1];
	DECLARE_BITMAP(used_bufs, VIRTIO_PCIDEV_WRITE_BUFS);

#define VIRTIO_PCIDEV_STAT_WAITING	0
	unsigned long status;

	bool platform;
};

static unsigned int virtio_pcidev_max_delay_us = 40000;
module_param_named(max_delay_us, virtio_pcidev_max_delay_us, uint, 0644);

static int virtio_pcidev_get_buf(struct virtio_pcidev_device *dev, bool *posted)
{
	int i;

	for (i = 0; i < VIRTIO_PCIDEV_WRITE_BUFS; i++) {
		if (!test_and_set_bit(i, dev->used_bufs))
			return i;
	}

	*posted = false;
	return VIRTIO_PCIDEV_WRITE_BUFS;
}

static void virtio_pcidev_free_buf(struct virtio_pcidev_device *dev, void *buf)
{
	int i;

	if (buf == &dev->bufs[VIRTIO_PCIDEV_WRITE_BUFS]) {
		kfree(dev->extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS]);
		dev->extra_ptrs[VIRTIO_PCIDEV_WRITE_BUFS] = NULL;
		return;
	}

	for (i = 0; i < VIRTIO_PCIDEV_WRITE_BUFS; i++) {
		if (buf == &dev->bufs[i]) {
			kfree(dev->extra_ptrs[i]);
			dev->extra_ptrs[i] = NULL;
			WARN_ON(!test_and_clear_bit(i, dev->used_bufs));
			return;
		}
	}

	WARN_ON(1);
}

static int virtio_pcidev_send_cmd(struct virtio_pcidev_device *dev,
				  struct virtio_pcidev_msg *cmd,
				  unsigned int cmd_size,
				  const void *extra, unsigned int extra_size,
				  void *out, unsigned int out_size)
{
	struct scatterlist out_sg, extra_sg, in_sg;
	struct scatterlist *sgs_list[] = {
		[0] = &out_sg,
		[1] = extra ? &extra_sg : &in_sg,
		[2] = extra ? &in_sg : NULL,
	};
	struct virtio_pcidev_message_buffer *buf;
	int delay_count = 0;
	bool bounce_out;
	int ret, len;
	int buf_idx;
	bool posted;

	if (WARN_ON(cmd_size < sizeof(*cmd) || cmd_size > sizeof(*buf)))
		return -EINVAL;

	switch (cmd->op) {
	case VIRTIO_PCIDEV_OP_CFG_WRITE:
	case VIRTIO_PCIDEV_OP_MMIO_WRITE:
	case VIRTIO_PCIDEV_OP_MMIO_MEMSET:
		/* in PCI, writes are posted, so don't wait */
		posted = !out;
		WARN_ON(!posted);
		break;

Annotation

Implementation Notes