drm/msm: shrinker support
For a first step, only purge obj->madv==DONTNEED objects. We could be more agressive and next try unpinning inactive objects.. but that is only useful if you have swap. Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
parent
4fe5f65e66
commit
68209390f1
|
@ -46,6 +46,7 @@ msm-y := \
|
|||
msm_fence.o \
|
||||
msm_gem.o \
|
||||
msm_gem_prime.o \
|
||||
msm_gem_shrinker.o \
|
||||
msm_gem_submit.o \
|
||||
msm_gpu.o \
|
||||
msm_iommu.o \
|
||||
|
|
|
@ -195,6 +195,8 @@ static int msm_drm_uninit(struct device *dev)
|
|||
kfree(vbl_ev);
|
||||
}
|
||||
|
||||
msm_gem_shrinker_cleanup(ddev);
|
||||
|
||||
drm_kms_helper_poll_fini(ddev);
|
||||
|
||||
drm_dev_unregister(ddev);
|
||||
|
@ -350,6 +352,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
|
|||
}
|
||||
|
||||
ddev->dev_private = priv;
|
||||
priv->dev = ddev;
|
||||
|
||||
ret = msm_mdss_init(ddev);
|
||||
if (ret) {
|
||||
|
@ -382,6 +385,8 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
|
|||
if (ret)
|
||||
goto fail;
|
||||
|
||||
msm_gem_shrinker_init(ddev);
|
||||
|
||||
switch (get_mdp_ver(pdev)) {
|
||||
case 4:
|
||||
kms = mdp4_kms_init(ddev);
|
||||
|
|
|
@ -78,6 +78,8 @@ struct msm_vblank_ctrl {
|
|||
|
||||
struct msm_drm_private {
|
||||
|
||||
struct drm_device *dev;
|
||||
|
||||
struct msm_kms *kms;
|
||||
|
||||
/* subordinate devices, if present: */
|
||||
|
@ -151,6 +153,8 @@ struct msm_drm_private {
|
|||
struct drm_mm mm;
|
||||
} vram;
|
||||
|
||||
struct shrinker shrinker;
|
||||
|
||||
struct msm_vblank_ctrl vblank_ctrl;
|
||||
};
|
||||
|
||||
|
@ -169,6 +173,9 @@ void msm_gem_submit_free(struct msm_gem_submit *submit);
|
|||
int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
void msm_gem_shrinker_init(struct drm_device *dev);
|
||||
void msm_gem_shrinker_cleanup(struct drm_device *dev);
|
||||
|
||||
int msm_gem_mmap_obj(struct drm_gem_object *obj,
|
||||
struct vm_area_struct *vma);
|
||||
int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
|
@ -196,6 +203,7 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj);
|
|||
void *msm_gem_vaddr_locked(struct drm_gem_object *obj);
|
||||
void *msm_gem_vaddr(struct drm_gem_object *obj);
|
||||
int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv);
|
||||
void msm_gem_purge(struct drm_gem_object *obj);
|
||||
int msm_gem_sync_object(struct drm_gem_object *obj,
|
||||
struct msm_fence_context *fctx, bool exclusive);
|
||||
void msm_gem_move_to_active(struct drm_gem_object *obj,
|
||||
|
|
|
@ -448,6 +448,38 @@ int msm_gem_madvise(struct drm_gem_object *obj, unsigned madv)
|
|||
return (msm_obj->madv != __MSM_MADV_PURGED);
|
||||
}
|
||||
|
||||
void msm_gem_purge(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
WARN_ON(!is_purgeable(msm_obj));
|
||||
WARN_ON(obj->import_attach);
|
||||
|
||||
put_iova(obj);
|
||||
|
||||
vunmap(msm_obj->vaddr);
|
||||
msm_obj->vaddr = NULL;
|
||||
|
||||
put_pages(obj);
|
||||
|
||||
msm_obj->madv = __MSM_MADV_PURGED;
|
||||
|
||||
drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping);
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
|
||||
/* Our goal here is to return as much of the memory as
|
||||
* is possible back to the system as we are called from OOM.
|
||||
* To do this we must instruct the shmfs to drop all of its
|
||||
* backing pages, *now*.
|
||||
*/
|
||||
shmem_truncate_range(file_inode(obj->filp), 0, (loff_t)-1);
|
||||
|
||||
invalidate_mapping_pages(file_inode(obj->filp)->i_mapping,
|
||||
0, (loff_t)-1);
|
||||
}
|
||||
|
||||
/* must be called before _move_to_active().. */
|
||||
int msm_gem_sync_object(struct drm_gem_object *obj,
|
||||
struct msm_fence_context *fctx, bool exclusive)
|
||||
|
|
|
@ -77,6 +77,12 @@ static inline bool is_active(struct msm_gem_object *msm_obj)
|
|||
return msm_obj->gpu != NULL;
|
||||
}
|
||||
|
||||
static inline bool is_purgeable(struct msm_gem_object *msm_obj)
|
||||
{
|
||||
return (msm_obj->madv == MSM_MADV_DONTNEED) && msm_obj->sgt &&
|
||||
!msm_obj->base.dma_buf && !msm_obj->base.import_attach;
|
||||
}
|
||||
|
||||
#define MAX_CMDS 4
|
||||
|
||||
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Red Hat
|
||||
* Author: Rob Clark <robdclark@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_gem.h"
|
||||
|
||||
static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
|
||||
{
|
||||
if (!mutex_is_locked(mutex))
|
||||
return false;
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
|
||||
return mutex->owner == task;
|
||||
#else
|
||||
/* Since UP may be pre-empted, we cannot assume that we own the lock */
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool msm_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
|
||||
{
|
||||
if (!mutex_trylock(&dev->struct_mutex)) {
|
||||
if (!mutex_is_locked_by(&dev->struct_mutex, current))
|
||||
return false;
|
||||
*unlock = false;
|
||||
} else {
|
||||
*unlock = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long
|
||||
msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
{
|
||||
struct msm_drm_private *priv =
|
||||
container_of(shrinker, struct msm_drm_private, shrinker);
|
||||
struct drm_device *dev = priv->dev;
|
||||
struct msm_gem_object *msm_obj;
|
||||
unsigned long count = 0;
|
||||
bool unlock;
|
||||
|
||||
if (!msm_gem_shrinker_lock(dev, &unlock))
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) {
|
||||
if (is_purgeable(msm_obj))
|
||||
count += msm_obj->base.size >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if (unlock)
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
{
|
||||
struct msm_drm_private *priv =
|
||||
container_of(shrinker, struct msm_drm_private, shrinker);
|
||||
struct drm_device *dev = priv->dev;
|
||||
struct msm_gem_object *msm_obj;
|
||||
unsigned long freed = 0;
|
||||
bool unlock;
|
||||
|
||||
if (!msm_gem_shrinker_lock(dev, &unlock))
|
||||
return SHRINK_STOP;
|
||||
|
||||
list_for_each_entry(msm_obj, &priv->inactive_list, mm_list) {
|
||||
if (freed >= sc->nr_to_scan)
|
||||
break;
|
||||
if (is_purgeable(msm_obj)) {
|
||||
msm_gem_purge(&msm_obj->base);
|
||||
freed += msm_obj->base.size >> PAGE_SHIFT;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlock)
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (freed > 0)
|
||||
pr_info_ratelimited("Purging %lu bytes\n", freed << PAGE_SHIFT);
|
||||
|
||||
return freed;
|
||||
}
|
||||
|
||||
/**
|
||||
* msm_gem_shrinker_init - Initialize msm shrinker
|
||||
* @dev_priv: msm device
|
||||
*
|
||||
* This function registers and sets up the msm shrinker.
|
||||
*/
|
||||
void msm_gem_shrinker_init(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
priv->shrinker.count_objects = msm_gem_shrinker_count;
|
||||
priv->shrinker.scan_objects = msm_gem_shrinker_scan;
|
||||
priv->shrinker.seeks = DEFAULT_SEEKS;
|
||||
WARN_ON(register_shrinker(&priv->shrinker));
|
||||
}
|
||||
|
||||
/**
|
||||
* msm_gem_shrinker_cleanup - Clean up msm shrinker
|
||||
* @dev_priv: msm device
|
||||
*
|
||||
* This function unregisters the msm shrinker.
|
||||
*/
|
||||
void msm_gem_shrinker_cleanup(struct drm_device *dev)
|
||||
{
|
||||
struct msm_drm_private *priv = dev->dev_private;
|
||||
unregister_shrinker(&priv->shrinker);
|
||||
}
|
Loading…
Reference in New Issue