blob: b0f36bd3444ee2cd681e96b200935cb1eddbbb27 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Records the mapped device addresses.
*
* Copyright (C) 2020 Google LLC
*/
#ifndef __GXP_MAPPING_H__
#define __GXP_MAPPING_H__
#include <linux/dma-direction.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#include "gxp-internal.h"
#if IS_ENABLED(CONFIG_GXP_TEST)
/* expose this variable to have unit tests set it dynamically */
extern bool gxp_log_iova;
#endif
#define GXP_IOVA_LOG_UNMAP (0u << 0)
#define GXP_IOVA_LOG_MAP (1u << 0)
#define GXP_IOVA_LOG_BUFFER (0u << 1)
#define GXP_IOVA_LOG_DMABUF (1u << 1)
struct gxp_mapping {
struct rb_node node;
refcount_t refcount;
void (*destructor)(struct gxp_mapping *mapping);
/*
* User-space address of the mapped buffer.
* If this value is 0, it indicates this mapping is for a dma-buf and
* should not be used if a regular buffer mapping was expected.
*/
u64 host_address;
struct gxp_dev *gxp;
struct gcip_iommu_domain *domain;
/*
* `device_address` and `size` are the base address and size of the
* user buffer a mapping represents.
*
* Due to alignment requirements from hardware, the actual IOVA space
* allocated may be larger and start at a different address, but that
* information is contained in the scatter-gather table, `sgt` below.
*/
dma_addr_t device_address;
size_t size;
uint gxp_dma_flags;
enum dma_data_direction dir;
struct sg_table sgt;
/* A mapping can only be synced by one thread at a time */
struct mutex sync_lock;
/*
* `virtual_address` and `page_count` are set when gxp_mapping_vmap(..)
* is called, and unset when gxp_mapping_vunmap(..) is called
*/
void *virtual_address;
u32 page_count;
uint vmap_count;
/* Protects `virtual_address`, `page_count`, and `vmap_count` */
struct mutex vlock;
};
/**
* gxp_mapping_iova_log() - Log IOVA mapping details
* @client: The client to create/destroy the mapping for
* @map: The mapping being handled
* @mask: The mask combination of GXP_IOVA_LOG_*
*
* Log IOVA mapping details for each map/unmap operation.
* Log the field names of the data before first mapping is logged.
*/
void gxp_mapping_iova_log(struct gxp_client *client, struct gxp_mapping *map,
u8 mask);
/**
* gxp_mapping_create() - Create a mapping for a user buffer
* @gxp: The GXP device to create the mapping for
* @domain: The iommu domain the mapping for
* @user_address: The user-space address of the buffer to map
* @size: The size of the buffer to be mapped
* @flags: Flags describing the type of mapping to create; currently unused
* @dir: DMA direction
*
* Upon successful creation, the mapping will be created with a reference count
* of 1.
*
* Return: A pointer to the newly created mapping on success; otherwise an
* ERR_PTR:
* * -ENOMEM: Insufficient memory to create the mapping
* * -EFAULT: Unable to pin the user pages
* * -EINVAL: Attempting to map read-only pages for writing by device or failed
* to map the buffer for the device.
*/
struct gxp_mapping *gxp_mapping_create(struct gxp_dev *gxp,
struct gcip_iommu_domain *domain,
u64 user_address, size_t size, u32 flags,
enum dma_data_direction dir);
/**
* gxp_mapping_get() - Increment a mapping's reference count
* @map: The mapping to obtain a reference to
*
* Return: True if the mapping's reference count was non-zero and incremented
* successfully; false otherwise.
*/
bool gxp_mapping_get(struct gxp_mapping *mapping);
/**
* gxp_mapping_put() - Decrement a mapping's reference
* @mapping: The mapping to release a reference to
*/
void gxp_mapping_put(struct gxp_mapping *mapping);
/**
* gxp_mapping_sync() - Sync a mapped buffer for either CPU or device
* @mapping: The mapping to sync
* @offset: The offset, in bytes, into the mapped buffer where the region to
* be synced begins
* @size: The size, in bytes, of the region to be synced
* @for_cpu: True to sync for CPU access (cache invalidate), false to sync for
* device access (cache flush)
*
* Return:
* * 0: Success
* * -ENODEV: A reference to the mapping could not be obtained
* * -EINVAL: The specified @offset and @size were not valid
*/
int gxp_mapping_sync(struct gxp_mapping *mapping, u32 offset, u32 size,
bool for_cpu);
/**
* gxp_mapping_vmap() - Map a mapping's buffer into kernel address space
* @mapping: The mapping to map into kernel space
* @is_dmabuf: Whether or not the mapping is for a dmabuf
*
* If the buffer is already mapped, increments a reference count and returns
* the existing virtual address instead.
*
* Obtains a reference to @mapping if the buffer had not been mapped yet.
*
* Return: A pointer to the mapped buffer if successful; otherwise an ERR_PTR:
* * -ENODEV: A reference to the mapping could not be obtained
* * -ENOMEM: Insufficient memory to map the buffer
* * -EINVAL: No valid sgt found.
*/
void *gxp_mapping_vmap(struct gxp_mapping *mapping, bool is_dmabuf);
/**
* gxp_mapping_vunmap() - Unmap a mapping from kernel address space
* @mapping: The mapping to unmap from kernel space
*
* Decrements the mapping's vmap reference count, and unmaps the buffer if that
* count drops to zero.
*
* Releases a reference to @mapping if the buffer is unmapped
*/
void gxp_mapping_vunmap(struct gxp_mapping *mapping);
#endif /* __GXP_MAPPING_H__ */