162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT 262306a36Sopenharmony_ci/************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2007-2010 VMware, Inc., Palo Alto, CA., USA 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the 862306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 962306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1062306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1162306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1262306a36Sopenharmony_ci * the following conditions: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1562306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1662306a36Sopenharmony_ci * of the Software. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1962306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2062306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2162306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2262306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2362306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2462306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci **************************************************************************/ 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "vmwgfx_drv.h" 3262306a36Sopenharmony_ci#include <drm/ttm/ttm_placement.h> 3362306a36Sopenharmony_ci#include <linux/idr.h> 3462306a36Sopenharmony_ci#include <linux/spinlock.h> 3562306a36Sopenharmony_ci#include <linux/kernel.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct vmwgfx_gmrid_man { 3862306a36Sopenharmony_ci struct ttm_resource_manager manager; 3962306a36Sopenharmony_ci spinlock_t lock; 4062306a36Sopenharmony_ci struct ida gmr_ida; 4162306a36Sopenharmony_ci uint32_t max_gmr_ids; 4262306a36Sopenharmony_ci uint32_t max_gmr_pages; 4362306a36Sopenharmony_ci uint32_t used_gmr_pages; 4462306a36Sopenharmony_ci uint8_t type; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct vmwgfx_gmrid_man *to_gmrid_manager(struct ttm_resource_manager *man) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci return container_of(man, struct vmwgfx_gmrid_man, manager); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int vmw_gmrid_man_get_node(struct ttm_resource_manager *man, 5362306a36Sopenharmony_ci struct ttm_buffer_object *bo, 5462306a36Sopenharmony_ci const struct ttm_place *place, 5562306a36Sopenharmony_ci struct ttm_resource **res) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); 5862306a36Sopenharmony_ci int id; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci *res = kmalloc(sizeof(**res), GFP_KERNEL); 6162306a36Sopenharmony_ci if (!*res) 6262306a36Sopenharmony_ci return -ENOMEM; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ttm_resource_init(bo, place, *res); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL); 6762306a36Sopenharmony_ci if (id < 0) { 6862306a36Sopenharmony_ci ttm_resource_fini(man, *res); 6962306a36Sopenharmony_ci kfree(*res); 7062306a36Sopenharmony_ci return id; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci spin_lock(&gman->lock); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (gman->max_gmr_pages > 0) { 7662306a36Sopenharmony_ci gman->used_gmr_pages += PFN_UP((*res)->size); 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Because the graphics memory is a soft limit we can try to 7962306a36Sopenharmony_ci * expand it instead of letting the userspace apps crash. 8062306a36Sopenharmony_ci * We're just going to have a sane limit (half of RAM) 8162306a36Sopenharmony_ci * on the number of MOB's that we create and will try to keep 8262306a36Sopenharmony_ci * the system running until we reach that. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ci if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) { 8562306a36Sopenharmony_ci const unsigned long max_graphics_pages = totalram_pages() / 2; 8662306a36Sopenharmony_ci uint32_t new_max_pages = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci DRM_WARN("vmwgfx: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); 8962306a36Sopenharmony_ci vmw_host_printf("vmwgfx, warning: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (gman->max_gmr_pages > (max_graphics_pages / 2)) { 9262306a36Sopenharmony_ci DRM_WARN("vmwgfx: guest requires more than half of RAM for graphics.\n"); 9362306a36Sopenharmony_ci new_max_pages = max_graphics_pages; 9462306a36Sopenharmony_ci } else 9562306a36Sopenharmony_ci new_max_pages = gman->max_gmr_pages * 2; 9662306a36Sopenharmony_ci if (new_max_pages > gman->max_gmr_pages && new_max_pages >= gman->used_gmr_pages) { 9762306a36Sopenharmony_ci DRM_WARN("vmwgfx: increasing guest mob limits to %u kB.\n", 9862306a36Sopenharmony_ci ((new_max_pages) << (PAGE_SHIFT - 10))); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci gman->max_gmr_pages = new_max_pages; 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci char buf[256]; 10362306a36Sopenharmony_ci snprintf(buf, sizeof(buf), 10462306a36Sopenharmony_ci "vmwgfx, error: guest graphics is out of memory (mob limit at: %ukB).\n", 10562306a36Sopenharmony_ci ((gman->max_gmr_pages) << (PAGE_SHIFT - 10))); 10662306a36Sopenharmony_ci vmw_host_printf(buf); 10762306a36Sopenharmony_ci DRM_WARN("%s", buf); 10862306a36Sopenharmony_ci goto nospace; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci (*res)->start = id; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci spin_unlock(&gman->lock); 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cinospace: 11962306a36Sopenharmony_ci gman->used_gmr_pages -= PFN_UP((*res)->size); 12062306a36Sopenharmony_ci spin_unlock(&gman->lock); 12162306a36Sopenharmony_ci ida_free(&gman->gmr_ida, id); 12262306a36Sopenharmony_ci ttm_resource_fini(man, *res); 12362306a36Sopenharmony_ci kfree(*res); 12462306a36Sopenharmony_ci return -ENOSPC; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void vmw_gmrid_man_put_node(struct ttm_resource_manager *man, 12862306a36Sopenharmony_ci struct ttm_resource *res) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ida_free(&gman->gmr_ida, res->start); 13362306a36Sopenharmony_ci spin_lock(&gman->lock); 13462306a36Sopenharmony_ci gman->used_gmr_pages -= PFN_UP(res->size); 13562306a36Sopenharmony_ci spin_unlock(&gman->lock); 13662306a36Sopenharmony_ci ttm_resource_fini(man, res); 13762306a36Sopenharmony_ci kfree(res); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void vmw_gmrid_man_debug(struct ttm_resource_manager *man, 14162306a36Sopenharmony_ci struct drm_printer *printer) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci BUG_ON(gman->type != VMW_PL_GMR && gman->type != VMW_PL_MOB); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci drm_printf(printer, "%s's used: %u pages, max: %u pages, %u id's\n", 14862306a36Sopenharmony_ci (gman->type == VMW_PL_MOB) ? "Mob" : "GMR", 14962306a36Sopenharmony_ci gman->used_gmr_pages, gman->max_gmr_pages, gman->max_gmr_ids); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct ttm_resource_manager_func vmw_gmrid_manager_func; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint vmw_gmrid_man_init(struct vmw_private *dev_priv, int type) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct ttm_resource_manager *man; 15762306a36Sopenharmony_ci struct vmwgfx_gmrid_man *gman = 15862306a36Sopenharmony_ci kzalloc(sizeof(*gman), GFP_KERNEL); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (unlikely(!gman)) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci man = &gman->manager; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci man->func = &vmw_gmrid_manager_func; 16662306a36Sopenharmony_ci man->use_tt = true; 16762306a36Sopenharmony_ci ttm_resource_manager_init(man, &dev_priv->bdev, 0); 16862306a36Sopenharmony_ci spin_lock_init(&gman->lock); 16962306a36Sopenharmony_ci gman->used_gmr_pages = 0; 17062306a36Sopenharmony_ci ida_init(&gman->gmr_ida); 17162306a36Sopenharmony_ci gman->type = type; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci switch (type) { 17462306a36Sopenharmony_ci case VMW_PL_GMR: 17562306a36Sopenharmony_ci gman->max_gmr_ids = dev_priv->max_gmr_ids; 17662306a36Sopenharmony_ci gman->max_gmr_pages = dev_priv->max_gmr_pages; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case VMW_PL_MOB: 17962306a36Sopenharmony_ci gman->max_gmr_ids = VMWGFX_NUM_MOB; 18062306a36Sopenharmony_ci gman->max_gmr_pages = dev_priv->max_mob_pages; 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci default: 18362306a36Sopenharmony_ci BUG(); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci ttm_set_driver_manager(&dev_priv->bdev, type, &gman->manager); 18662306a36Sopenharmony_ci ttm_resource_manager_set_used(man, true); 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid vmw_gmrid_man_fini(struct vmw_private *dev_priv, int type) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, type); 19362306a36Sopenharmony_ci struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ttm_resource_manager_set_used(man, false); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ttm_resource_manager_evict_all(&dev_priv->bdev, man); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ttm_resource_manager_cleanup(man); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ttm_set_driver_manager(&dev_priv->bdev, type, NULL); 20262306a36Sopenharmony_ci ida_destroy(&gman->gmr_ida); 20362306a36Sopenharmony_ci kfree(gman); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic const struct ttm_resource_manager_func vmw_gmrid_manager_func = { 20862306a36Sopenharmony_ci .alloc = vmw_gmrid_man_get_node, 20962306a36Sopenharmony_ci .free = vmw_gmrid_man_put_node, 21062306a36Sopenharmony_ci .debug = vmw_gmrid_man_debug 21162306a36Sopenharmony_ci}; 212