drivers/s390/cio/vfio_ccw_cp.c

Source file repositories/reference/linux-study-clean/drivers/s390/cio/vfio_ccw_cp.c

File Facts

System
Linux kernel
Corpus path
drivers/s390/cio/vfio_ccw_cp.c
Extension
.c
Size
25311 bytes
Lines
964
Domain
Driver Families
Bucket
drivers/s390
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct page_array {
	/* Array that stores pages need to pin. */
	dma_addr_t		*pa_iova;
	/* Array that receives the pinned pages. */
	struct page		**pa_page;
	/* Number of pages pinned from @pa_iova. */
	int			pa_nr;
};

struct ccwchain {
	struct list_head	next;
	struct ccw1		*ch_ccw;
	/* Guest physical address of the current chain. */
	u64			ch_iova;
	/* Count of the valid ccws in chain. */
	int			ch_len;
	/* Pinned PAGEs for the original data. */
	struct page_array	*ch_pa;
};

/*
 * page_array_alloc() - alloc memory for page array
 * @pa: page_array on which to perform the operation
 * @len: number of pages that should be pinned from @iova
 *
 * Attempt to allocate memory for page array.
 *
 * Usage of page_array:
 * We expect (pa_nr == 0) and (pa_iova == NULL), any field in
 * this structure will be filled in by this function.
 *
 * Returns:
 *         0 if page array is allocated
 *   -EINVAL if pa->pa_nr is not initially zero, or pa->pa_iova is not NULL
 *   -ENOMEM if alloc failed
 */
static int page_array_alloc(struct page_array *pa, unsigned int len)
{
	if (pa->pa_nr || pa->pa_iova)
		return -EINVAL;

	if (len == 0)
		return -EINVAL;

	pa->pa_nr = len;

	pa->pa_iova = kzalloc_objs(*pa->pa_iova, len);
	if (!pa->pa_iova)
		return -ENOMEM;

	pa->pa_page = kzalloc_objs(*pa->pa_page, len);
	if (!pa->pa_page) {
		kfree(pa->pa_iova);
		return -ENOMEM;
	}

	return 0;
}

/*
 * page_array_unpin() - Unpin user pages in memory
 * @pa: page_array on which to perform the operation
 * @vdev: the vfio device to perform the operation
 * @pa_nr: number of user pages to unpin
 * @unaligned: were pages unaligned on the pin request
 *
 * Only unpin if any pages were pinned to begin with, i.e. pa_nr > 0,
 * otherwise only clear pa->pa_nr
 */
static void page_array_unpin(struct page_array *pa,
			     struct vfio_device *vdev, int pa_nr, bool unaligned)
{
	int unpinned = 0, npage = 1;

	while (unpinned < pa_nr) {
		dma_addr_t *first = &pa->pa_iova[unpinned];
		dma_addr_t *last = &first[npage];

		if (unpinned + npage < pa_nr &&
		    *first + npage * PAGE_SIZE == *last &&
		    !unaligned) {
			npage++;
			continue;
		}

		vfio_unpin_pages(vdev, *first, npage);
		unpinned += npage;
		npage = 1;
	}

Annotation

Implementation Notes