diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c')
| -rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c | 208 |
1 files changed, 129 insertions, 79 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index c5c054ae9056..5bd967fbcf55 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT /************************************************************************** * - * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA - * All Rights Reserved. + * Copyright 2007-2010 VMware, Inc., Palo Alto, CA., USA * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the @@ -29,133 +29,183 @@ */ #include "vmwgfx_drv.h" -#include <drm/ttm/ttm_module.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> #include <linux/idr.h> #include <linux/spinlock.h> #include <linux/kernel.h> struct vmwgfx_gmrid_man { + struct ttm_resource_manager manager; spinlock_t lock; struct ida gmr_ida; uint32_t max_gmr_ids; uint32_t max_gmr_pages; uint32_t used_gmr_pages; + uint8_t type; }; -static int vmw_gmrid_man_get_node(struct ttm_mem_type_manager *man, +static struct vmwgfx_gmrid_man *to_gmrid_manager(struct ttm_resource_manager *man) +{ + return container_of(man, struct vmwgfx_gmrid_man, manager); +} + +static int vmw_gmrid_man_get_node(struct ttm_resource_manager *man, struct ttm_buffer_object *bo, - struct ttm_placement *placement, - struct ttm_mem_reg *mem) + const struct ttm_place *place, + struct ttm_resource **res) { - struct vmwgfx_gmrid_man *gman = - (struct vmwgfx_gmrid_man *)man->priv; - int ret = 0; + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); int id; - mem->mm_node = NULL; + *res = kmalloc(sizeof(**res), GFP_KERNEL); + if (!*res) + return -ENOMEM; - spin_lock(&gman->lock); + ttm_resource_init(bo, place, *res); - if (gman->max_gmr_pages > 0) { - gman->used_gmr_pages += bo->num_pages; - if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) - goto out_err_locked; + id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL); + if (id < 0) { + ttm_resource_fini(man, *res); + kfree(*res); + return id; } - do { - spin_unlock(&gman->lock); - if (unlikely(ida_pre_get(&gman->gmr_ida, GFP_KERNEL) == 0)) { - ret = -ENOMEM; - goto out_err; - } - spin_lock(&gman->lock); + spin_lock(&gman->lock); - ret = ida_get_new(&gman->gmr_ida, &id); - if (unlikely(ret == 0 && id >= gman->max_gmr_ids)) { - ida_remove(&gman->gmr_ida, id); - ret = 0; - goto out_err_locked; + if (gman->max_gmr_pages > 0) { + gman->used_gmr_pages += PFN_UP((*res)->size); + /* + * Because the graphics memory is a soft limit we can try to + * expand it instead of letting the userspace apps crash. + * We're just going to have a sane limit (half of RAM) + * on the number of MOB's that we create and will try to keep + * the system running until we reach that. + */ + if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) { + const unsigned long max_graphics_pages = totalram_pages() / 2; + uint32_t new_max_pages = 0; + + DRM_WARN("vmwgfx: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); + vmw_host_printf("vmwgfx, warning: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); + + if (gman->max_gmr_pages > (max_graphics_pages / 2)) { + DRM_WARN("vmwgfx: guest requires more than half of RAM for graphics.\n"); + new_max_pages = max_graphics_pages; + } else + new_max_pages = gman->max_gmr_pages * 2; + if (new_max_pages > gman->max_gmr_pages && new_max_pages >= gman->used_gmr_pages) { + DRM_WARN("vmwgfx: increasing guest mob limits to %u KiB.\n", + ((new_max_pages) << (PAGE_SHIFT - 10))); + + gman->max_gmr_pages = new_max_pages; + } else { + char buf[256]; + snprintf(buf, sizeof(buf), + "vmwgfx, error: guest graphics is out of memory (mob limit at: %u KiB).\n", + ((gman->max_gmr_pages) << (PAGE_SHIFT - 10))); + vmw_host_printf(buf); + DRM_WARN("%s", buf); + goto nospace; + } } - } while (ret == -EAGAIN); + } - if (likely(ret == 0)) { - mem->mm_node = gman; - mem->start = id; - mem->num_pages = bo->num_pages; - } else - goto out_err_locked; + (*res)->start = id; spin_unlock(&gman->lock); return 0; -out_err: +nospace: + gman->used_gmr_pages -= PFN_UP((*res)->size); + spin_unlock(&gman->lock); + ida_free(&gman->gmr_ida, id); + ttm_resource_fini(man, *res); + kfree(*res); + return -ENOSPC; +} + +static void vmw_gmrid_man_put_node(struct ttm_resource_manager *man, + struct ttm_resource *res) +{ + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + + ida_free(&gman->gmr_ida, res->start); spin_lock(&gman->lock); -out_err_locked: - gman->used_gmr_pages -= bo->num_pages; + gman->used_gmr_pages -= PFN_UP(res->size); spin_unlock(&gman->lock); - return ret; + ttm_resource_fini(man, res); + kfree(res); } -static void vmw_gmrid_man_put_node(struct ttm_mem_type_manager *man, - struct ttm_mem_reg *mem) +static void vmw_gmrid_man_debug(struct ttm_resource_manager *man, + struct drm_printer *printer) { - struct vmwgfx_gmrid_man *gman = - (struct vmwgfx_gmrid_man *)man->priv; - - if (mem->mm_node) { - spin_lock(&gman->lock); - ida_remove(&gman->gmr_ida, mem->start); - gman->used_gmr_pages -= mem->num_pages; - spin_unlock(&gman->lock); - mem->mm_node = NULL; - } + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + + BUG_ON(gman->type != VMW_PL_GMR && gman->type != VMW_PL_MOB); + + drm_printf(printer, "%s's used: %u pages, max: %u pages, %u id's\n", + (gman->type == VMW_PL_MOB) ? "Mob" : "GMR", + gman->used_gmr_pages, gman->max_gmr_pages, gman->max_gmr_ids); } -static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man, - unsigned long p_size) +static const struct ttm_resource_manager_func vmw_gmrid_manager_func; + +int vmw_gmrid_man_init(struct vmw_private *dev_priv, int type) { - struct vmw_private *dev_priv = - container_of(man->bdev, struct vmw_private, bdev); + struct ttm_resource_manager *man; struct vmwgfx_gmrid_man *gman = kzalloc(sizeof(*gman), GFP_KERNEL); - if (unlikely(gman == NULL)) + if (unlikely(!gman)) return -ENOMEM; + man = &gman->manager; + + man->func = &vmw_gmrid_manager_func; + man->use_tt = true; + ttm_resource_manager_init(man, &dev_priv->bdev, 0); spin_lock_init(&gman->lock); - gman->max_gmr_pages = dev_priv->max_gmr_pages; gman->used_gmr_pages = 0; ida_init(&gman->gmr_ida); - gman->max_gmr_ids = p_size; - man->priv = (void *) gman; + gman->type = type; + + switch (type) { + case VMW_PL_GMR: + gman->max_gmr_ids = dev_priv->max_gmr_ids; + gman->max_gmr_pages = dev_priv->max_gmr_pages; + break; + case VMW_PL_MOB: + gman->max_gmr_ids = VMWGFX_NUM_MOB; + gman->max_gmr_pages = dev_priv->max_mob_pages; + break; + default: + BUG(); + } + ttm_set_driver_manager(&dev_priv->bdev, type, &gman->manager); + ttm_resource_manager_set_used(man, true); return 0; } -static int vmw_gmrid_man_takedown(struct ttm_mem_type_manager *man) +void vmw_gmrid_man_fini(struct vmw_private *dev_priv, int type) { - struct vmwgfx_gmrid_man *gman = - (struct vmwgfx_gmrid_man *)man->priv; + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, type); + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); - if (gman) { - ida_destroy(&gman->gmr_ida); - kfree(gman); - } - return 0; -} + ttm_resource_manager_set_used(man, false); + + ttm_resource_manager_evict_all(&dev_priv->bdev, man); + + ttm_resource_manager_cleanup(man); + + ttm_set_driver_manager(&dev_priv->bdev, type, NULL); + ida_destroy(&gman->gmr_ida); + kfree(gman); -static void vmw_gmrid_man_debug(struct ttm_mem_type_manager *man, - const char *prefix) -{ - printk(KERN_INFO "%s: No debug info available for the GMR " - "id manager.\n", prefix); } -const struct ttm_mem_type_manager_func vmw_gmrid_manager_func = { - vmw_gmrid_man_init, - vmw_gmrid_man_takedown, - vmw_gmrid_man_get_node, - vmw_gmrid_man_put_node, - vmw_gmrid_man_debug +static const struct ttm_resource_manager_func vmw_gmrid_manager_func = { + .alloc = vmw_gmrid_man_get_node, + .free = vmw_gmrid_man_put_node, + .debug = vmw_gmrid_man_debug }; |
