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

memory_mgr.c (8852B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3/*
      4 * Copyright 2022 HabanaLabs, Ltd.
      5 * All Rights Reserved.
      6 */
      7
      8#include "habanalabs.h"
      9
     10/**
     11 * hl_mmap_mem_buf_get - increase the buffer refcount and return a pointer to
     12 *                        the buffer descriptor.
     13 *
     14 * @mmg: parent unifed memory manager
     15 * @handle: requested buffer handle
     16 *
     17 * Find the buffer in the store and return a pointer to its descriptor.
     18 * Increase buffer refcount. If not found - return NULL.
     19 */
     20struct hl_mmap_mem_buf *hl_mmap_mem_buf_get(struct hl_mem_mgr *mmg, u64 handle)
     21{
     22	struct hl_mmap_mem_buf *buf;
     23
     24	spin_lock(&mmg->lock);
     25	buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
     26	if (!buf) {
     27		spin_unlock(&mmg->lock);
     28		dev_warn(mmg->dev,
     29			 "Buff get failed, no match to handle %#llx\n", handle);
     30		return NULL;
     31	}
     32	kref_get(&buf->refcount);
     33	spin_unlock(&mmg->lock);
     34	return buf;
     35}
     36
     37/**
     38 * hl_mmap_mem_buf_destroy - destroy the unused buffer
     39 *
     40 * @buf: memory manager buffer descriptor
     41 *
     42 * Internal function, used as a final step of buffer release. Shall be invoked
     43 * only when the buffer is no longer in use (removed from idr). Will call the
     44 * release callback (if applicable), and free the memory.
     45 */
     46static void hl_mmap_mem_buf_destroy(struct hl_mmap_mem_buf *buf)
     47{
     48	if (buf->behavior->release)
     49		buf->behavior->release(buf);
     50
     51	kfree(buf);
     52}
     53
     54/**
     55 * hl_mmap_mem_buf_release - release buffer
     56 *
     57 * @kref: kref that reached 0.
     58 *
     59 * Internal function, used as a kref release callback, when the last user of
     60 * the buffer is released. Shall be called from an interrupt context.
     61 */
     62static void hl_mmap_mem_buf_release(struct kref *kref)
     63{
     64	struct hl_mmap_mem_buf *buf =
     65		container_of(kref, struct hl_mmap_mem_buf, refcount);
     66
     67	spin_lock(&buf->mmg->lock);
     68	idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
     69	spin_unlock(&buf->mmg->lock);
     70
     71	hl_mmap_mem_buf_destroy(buf);
     72}
     73
     74/**
     75 * hl_mmap_mem_buf_remove_idr_locked - remove handle from idr
     76 *
     77 * @kref: kref that reached 0.
     78 *
     79 * Internal function, used for kref put by handle. Assumes mmg lock is taken.
     80 * Will remove the buffer from idr, without destroying it.
     81 */
     82static void hl_mmap_mem_buf_remove_idr_locked(struct kref *kref)
     83{
     84	struct hl_mmap_mem_buf *buf =
     85		container_of(kref, struct hl_mmap_mem_buf, refcount);
     86
     87	idr_remove(&buf->mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
     88}
     89
     90/**
     91 * hl_mmap_mem_buf_put - decrease the reference to the buffer
     92 *
     93 * @buf: memory manager buffer descriptor
     94 *
     95 * Decrease the reference to the buffer, and release it if it was the last one.
     96 * Shall be called from an interrupt context.
     97 */
     98int hl_mmap_mem_buf_put(struct hl_mmap_mem_buf *buf)
     99{
    100	return kref_put(&buf->refcount, hl_mmap_mem_buf_release);
    101}
    102
    103/**
    104 * hl_mmap_mem_buf_put_handle - decrease the reference to the buffer with the
    105 *                              given handle.
    106 *
    107 * @mmg: parent unifed memory manager
    108 * @handle: requested buffer handle
    109 *
    110 * Decrease the reference to the buffer, and release it if it was the last one.
    111 * Shall not be called from an interrupt context. Return -EINVAL if handle was
    112 * not found, else return the put outcome (0 or 1).
    113 */
    114int hl_mmap_mem_buf_put_handle(struct hl_mem_mgr *mmg, u64 handle)
    115{
    116	struct hl_mmap_mem_buf *buf;
    117
    118	spin_lock(&mmg->lock);
    119	buf = idr_find(&mmg->handles, lower_32_bits(handle >> PAGE_SHIFT));
    120	if (!buf) {
    121		spin_unlock(&mmg->lock);
    122		dev_dbg(mmg->dev,
    123			 "Buff put failed, no match to handle %#llx\n", handle);
    124		return -EINVAL;
    125	}
    126
    127	if (kref_put(&buf->refcount, hl_mmap_mem_buf_remove_idr_locked)) {
    128		spin_unlock(&mmg->lock);
    129		hl_mmap_mem_buf_destroy(buf);
    130		return 1;
    131	}
    132
    133	spin_unlock(&mmg->lock);
    134	return 0;
    135}
    136
    137/**
    138 * @hl_mmap_mem_buf_alloc - allocate a new mappable buffer
    139 *
    140 * @mmg: parent unifed memory manager
    141 * @behavior: behavior object describing this buffer polymorphic behavior
    142 * @gfp: gfp flags to use for the memory allocations
    143 * @args: additional args passed to behavior->alloc
    144 *
    145 * Allocate and register a new memory buffer inside the give memory manager.
    146 * Return the pointer to the new buffer on success or NULL on failure.
    147 */
    148struct hl_mmap_mem_buf *
    149hl_mmap_mem_buf_alloc(struct hl_mem_mgr *mmg,
    150		      struct hl_mmap_mem_buf_behavior *behavior, gfp_t gfp,
    151		      void *args)
    152{
    153	struct hl_mmap_mem_buf *buf;
    154	int rc;
    155
    156	buf = kzalloc(sizeof(*buf), gfp);
    157	if (!buf)
    158		return NULL;
    159
    160	spin_lock(&mmg->lock);
    161	rc = idr_alloc(&mmg->handles, buf, 1, 0, GFP_ATOMIC);
    162	spin_unlock(&mmg->lock);
    163	if (rc < 0) {
    164		dev_err(mmg->dev,
    165			"%s: Failed to allocate IDR for a new buffer, rc=%d\n",
    166			behavior->topic, rc);
    167		goto free_buf;
    168	}
    169
    170	buf->mmg = mmg;
    171	buf->behavior = behavior;
    172	buf->handle = (((u64)rc | buf->behavior->mem_id) << PAGE_SHIFT);
    173	kref_init(&buf->refcount);
    174
    175	rc = buf->behavior->alloc(buf, gfp, args);
    176	if (rc) {
    177		dev_err(mmg->dev, "%s: Failure in buffer alloc callback %d\n",
    178			behavior->topic, rc);
    179		goto remove_idr;
    180	}
    181
    182	return buf;
    183
    184remove_idr:
    185	spin_lock(&mmg->lock);
    186	idr_remove(&mmg->handles, lower_32_bits(buf->handle >> PAGE_SHIFT));
    187	spin_unlock(&mmg->lock);
    188free_buf:
    189	kfree(buf);
    190	return NULL;
    191}
    192
    193/**
    194 * hl_mmap_mem_buf_vm_close - handle mmap close
    195 *
    196 * @vma: the vma object for which mmap was closed.
    197 *
    198 * Put the memory buffer if it is no longer mapped.
    199 */
    200static void hl_mmap_mem_buf_vm_close(struct vm_area_struct *vma)
    201{
    202	struct hl_mmap_mem_buf *buf =
    203		(struct hl_mmap_mem_buf *)vma->vm_private_data;
    204	long new_mmap_size;
    205
    206	new_mmap_size = buf->real_mapped_size - (vma->vm_end - vma->vm_start);
    207
    208	if (new_mmap_size > 0) {
    209		buf->real_mapped_size = new_mmap_size;
    210		return;
    211	}
    212
    213	atomic_set(&buf->mmap, 0);
    214	hl_mmap_mem_buf_put(buf);
    215	vma->vm_private_data = NULL;
    216}
    217
    218static const struct vm_operations_struct hl_mmap_mem_buf_vm_ops = {
    219	.close = hl_mmap_mem_buf_vm_close
    220};
    221
    222/**
    223 * hl_mem_mgr_mmap - map the given buffer to the user
    224 *
    225 * @mmg: unifed memory manager
    226 * @vma: the vma object for which mmap was closed.
    227 * @args: additional args passed to behavior->mmap
    228 *
    229 * Map the buffer specified by the vma->vm_pgoff to the given vma.
    230 */
    231int hl_mem_mgr_mmap(struct hl_mem_mgr *mmg, struct vm_area_struct *vma,
    232		    void *args)
    233{
    234	struct hl_mmap_mem_buf *buf;
    235	u64 user_mem_size;
    236	u64 handle;
    237	int rc;
    238
    239	/* We use the page offset to hold the idr and thus we need to clear
    240	 * it before doing the mmap itself
    241	 */
    242	handle = vma->vm_pgoff << PAGE_SHIFT;
    243	vma->vm_pgoff = 0;
    244
    245	/* Reference was taken here */
    246	buf = hl_mmap_mem_buf_get(mmg, handle);
    247	if (!buf) {
    248		dev_err(mmg->dev,
    249			"Memory mmap failed, no match to handle %#llx\n", handle);
    250		return -EINVAL;
    251	}
    252
    253	/* Validation check */
    254	user_mem_size = vma->vm_end - vma->vm_start;
    255	if (user_mem_size != ALIGN(buf->mappable_size, PAGE_SIZE)) {
    256		dev_err(mmg->dev,
    257			"%s: Memory mmap failed, mmap VM size 0x%llx != 0x%llx allocated physical mem size\n",
    258			buf->behavior->topic, user_mem_size, buf->mappable_size);
    259		rc = -EINVAL;
    260		goto put_mem;
    261	}
    262
    263#ifdef _HAS_TYPE_ARG_IN_ACCESS_OK
    264	if (!access_ok(VERIFY_WRITE, (void __user *)(uintptr_t)vma->vm_start,
    265		       user_mem_size)) {
    266#else
    267	if (!access_ok((void __user *)(uintptr_t)vma->vm_start,
    268		       user_mem_size)) {
    269#endif
    270		dev_err(mmg->dev, "%s: User pointer is invalid - 0x%lx\n",
    271			buf->behavior->topic, vma->vm_start);
    272
    273		rc = -EINVAL;
    274		goto put_mem;
    275	}
    276
    277	if (atomic_cmpxchg(&buf->mmap, 0, 1)) {
    278		dev_err(mmg->dev,
    279			"%s, Memory mmap failed, already mmaped to user\n",
    280			buf->behavior->topic);
    281		rc = -EINVAL;
    282		goto put_mem;
    283	}
    284
    285	vma->vm_ops = &hl_mmap_mem_buf_vm_ops;
    286
    287	/* Note: We're transferring the memory reference to vma->vm_private_data here. */
    288
    289	vma->vm_private_data = buf;
    290
    291	rc = buf->behavior->mmap(buf, vma, args);
    292	if (rc) {
    293		atomic_set(&buf->mmap, 0);
    294		goto put_mem;
    295	}
    296
    297	buf->real_mapped_size = buf->mappable_size;
    298	vma->vm_pgoff = handle >> PAGE_SHIFT;
    299
    300	return 0;
    301
    302put_mem:
    303	hl_mmap_mem_buf_put(buf);
    304	return rc;
    305}
    306
    307/**
    308 * hl_mem_mgr_init - initialize unified memory manager
    309 *
    310 * @dev: owner device pointer
    311 * @mmg: structure to initialize
    312 *
    313 * Initialize an instance of unified memory manager
    314 */
    315void hl_mem_mgr_init(struct device *dev, struct hl_mem_mgr *mmg)
    316{
    317	mmg->dev = dev;
    318	spin_lock_init(&mmg->lock);
    319	idr_init(&mmg->handles);
    320}
    321
    322/**
    323 * hl_mem_mgr_fini - release unified memory manager
    324 *
    325 * @mmg: parent unifed memory manager
    326 *
    327 * Release the unified memory manager. Shall be called from an interrupt context.
    328 */
    329void hl_mem_mgr_fini(struct hl_mem_mgr *mmg)
    330{
    331	struct hl_mmap_mem_buf *buf;
    332	struct idr *idp;
    333	const char *topic;
    334	u32 id;
    335
    336	idp = &mmg->handles;
    337
    338	idr_for_each_entry(idp, buf, id) {
    339		topic = buf->behavior->topic;
    340		if (hl_mmap_mem_buf_put(buf) != 1)
    341			dev_err(mmg->dev,
    342				"%s: Buff handle %u for CTX is still alive\n",
    343				topic, id);
    344	}
    345
    346	/* TODO: can it happen that some buffer is still in use at this point? */
    347
    348	idr_destroy(&mmg->handles);
    349}