Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/gem: Introduce drm_gem_get_unmapped_area() fop

mmap() calls on the DRM file pointer currently always end up using
mm_get_unmapped_area() to get a free mapping region. On builds with
CONFIG_TRANSPARENT_HUGEPAGE enabled, this isn't ideal for GEM objects
backed by shmem buffers on mountpoints setting the 'huge=' option
because it can't correctly figure out the potentially huge address
alignment required.

This commit introduces the drm_gem_get_unmapped_area() function which
is meant to be used as a get_unmapped_area file operation on the DRM
file pointer to lookup GEM objects based on their fake offsets and get
a properly aligned region by calling shmem_get_unmapped_area() with
the right file pointer. If a GEM object isn't available at the given
offset or if the caller isn't granted access to it, the function falls
back to mm_get_unmapped_area().

This also makes drm_gem_get_unmapped_area() part of the default GEM
file operations so that all the DRM drivers can benefit from more
efficient mappings thanks to the huge page fault handler introduced in
previous commit 'drm/shmem-helper: Add huge page fault handler'.

The shmem_get_unmapped_area() function needs to be exported so that
it can be used from the DRM subsystem.

v3:
- include <linux/sched/mm.h> in drm_gem.c
- forward to shmem layer in builds with CONFIG_TRANSPARENT_HUGEPAGE=n

v6:
- use GPL variant to export drm_gem_get_unmapped_area()
- don't export shmem_get_unmapped_area() anymore (use f_op instead)

v11:
- rename drm_gem_object_lookup_from_offset() to
drm_gem_object_lookup_at_offset()
- add Boris R-b

Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Link: https://patch.msgid.link/20251205182231.194072-4-loic.molinari@collabora.com
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>

authored by

Loïc Molinari and committed by
Boris Brezillon
99bda20d 211b9a39

+90 -22
+86 -22
drivers/gpu/drm/drm_gem.c
··· 36 36 #include <linux/module.h> 37 37 #include <linux/pagemap.h> 38 38 #include <linux/pagevec.h> 39 + #include <linux/sched/mm.h> 39 40 #include <linux/shmem_fs.h> 40 41 #include <linux/slab.h> 41 42 #include <linux/string_helpers.h> ··· 1178 1177 } 1179 1178 EXPORT_SYMBOL(drm_gem_mmap_obj); 1180 1179 1181 - /** 1182 - * drm_gem_mmap - memory map routine for GEM objects 1183 - * @filp: DRM file pointer 1184 - * @vma: VMA for the area to be mapped 1185 - * 1186 - * If a driver supports GEM object mapping, mmap calls on the DRM file 1187 - * descriptor will end up here. 1188 - * 1189 - * Look up the GEM object based on the offset passed in (vma->vm_pgoff will 1190 - * contain the fake offset we created when the GTT map ioctl was called on 1191 - * the object) and map it with a call to drm_gem_mmap_obj(). 1192 - * 1193 - * If the caller is not granted access to the buffer object, the mmap will fail 1194 - * with EACCES. Please see the vma manager for more information. 1180 + /* 1181 + * Look up a GEM object in offset space based on the exact start address. The 1182 + * caller must be granted access to the object. Returns a GEM object on success 1183 + * or a negative error code on failure. The returned GEM object needs to be 1184 + * released with drm_gem_object_put(). 1195 1185 */ 1196 - int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 1186 + static struct drm_gem_object * 1187 + drm_gem_object_lookup_at_offset(struct file *filp, unsigned long start, 1188 + unsigned long pages) 1197 1189 { 1198 1190 struct drm_file *priv = filp->private_data; 1199 1191 struct drm_device *dev = priv->minor->dev; 1200 1192 struct drm_gem_object *obj = NULL; 1201 1193 struct drm_vma_offset_node *node; 1202 - int ret; 1203 1194 1204 1195 if (drm_dev_is_unplugged(dev)) 1205 - return -ENODEV; 1196 + return ERR_PTR(-ENODEV); 1206 1197 1207 1198 drm_vma_offset_lock_lookup(dev->vma_offset_manager); 1208 1199 node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, 1209 - vma->vm_pgoff, 1210 - vma_pages(vma)); 1200 + start, pages); 1211 1201 if (likely(node)) { 1212 1202 obj = container_of(node, struct drm_gem_object, vma_node); 1213 1203 /* ··· 1217 1225 drm_vma_offset_unlock_lookup(dev->vma_offset_manager); 1218 1226 1219 1227 if (!obj) 1220 - return -EINVAL; 1228 + return ERR_PTR(-EINVAL); 1221 1229 1222 1230 if (!drm_vma_node_is_allowed(node, priv)) { 1223 1231 drm_gem_object_put(obj); 1224 - return -EACCES; 1232 + return ERR_PTR(-EACCES); 1225 1233 } 1226 1234 1227 - ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT, 1235 + return obj; 1236 + } 1237 + 1238 + /** 1239 + * drm_gem_get_unmapped_area - get memory mapping region routine for GEM objects 1240 + * @filp: DRM file pointer 1241 + * @uaddr: User address hint 1242 + * @len: Mapping length 1243 + * @pgoff: Offset (in pages) 1244 + * @flags: Mapping flags 1245 + * 1246 + * If a driver supports GEM object mapping, before ending up in drm_gem_mmap(), 1247 + * mmap calls on the DRM file descriptor will first try to find a free linear 1248 + * address space large enough for a mapping. Since GEM objects are backed by 1249 + * shmem buffers, this should preferably be handled by the shmem virtual memory 1250 + * filesystem which can appropriately align addresses to huge page sizes when 1251 + * needed. 1252 + * 1253 + * Look up the GEM object based on the offset passed in (vma->vm_pgoff will 1254 + * contain the fake offset we created) and call shmem_get_unmapped_area() with 1255 + * the right file pointer. 1256 + * 1257 + * If a GEM object is not available at the given offset or if the caller is not 1258 + * granted access to it, fall back to mm_get_unmapped_area(). 1259 + */ 1260 + unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr, 1261 + unsigned long len, unsigned long pgoff, 1262 + unsigned long flags) 1263 + { 1264 + struct drm_gem_object *obj; 1265 + unsigned long ret; 1266 + 1267 + obj = drm_gem_object_lookup_at_offset(filp, pgoff, len >> PAGE_SHIFT); 1268 + if (IS_ERR(obj) || !obj->filp || !obj->filp->f_op->get_unmapped_area) 1269 + return mm_get_unmapped_area(current->mm, filp, uaddr, len, 0, 1270 + flags); 1271 + 1272 + ret = obj->filp->f_op->get_unmapped_area(obj->filp, uaddr, len, 0, 1273 + flags); 1274 + 1275 + drm_gem_object_put(obj); 1276 + 1277 + return ret; 1278 + } 1279 + EXPORT_SYMBOL_GPL(drm_gem_get_unmapped_area); 1280 + 1281 + /** 1282 + * drm_gem_mmap - memory map routine for GEM objects 1283 + * @filp: DRM file pointer 1284 + * @vma: VMA for the area to be mapped 1285 + * 1286 + * If a driver supports GEM object mapping, mmap calls on the DRM file 1287 + * descriptor will end up here. 1288 + * 1289 + * Look up the GEM object based on the offset passed in (vma->vm_pgoff will 1290 + * contain the fake offset we created) and map it with a call to 1291 + * drm_gem_mmap_obj(). 1292 + * 1293 + * If the caller is not granted access to the buffer object, the mmap will fail 1294 + * with EACCES. Please see the vma manager for more information. 1295 + */ 1296 + int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 1297 + { 1298 + struct drm_gem_object *obj; 1299 + int ret; 1300 + 1301 + obj = drm_gem_object_lookup_at_offset(filp, vma->vm_pgoff, 1302 + vma_pages(vma)); 1303 + if (IS_ERR(obj)) 1304 + return PTR_ERR(obj); 1305 + 1306 + ret = drm_gem_mmap_obj(obj, 1307 + drm_vma_node_size(&obj->vma_node) << PAGE_SHIFT, 1228 1308 vma); 1229 1309 1230 1310 drm_gem_object_put(obj);
+4
include/drm/drm_gem.h
··· 469 469 .poll = drm_poll,\ 470 470 .read = drm_read,\ 471 471 .llseek = noop_llseek,\ 472 + .get_unmapped_area = drm_gem_get_unmapped_area,\ 472 473 .mmap = drm_gem_mmap, \ 473 474 .fop_flags = FOP_UNSIGNED_OFFSET 474 475 ··· 507 506 int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, 508 507 struct vm_area_struct *vma); 509 508 int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); 509 + unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr, 510 + unsigned long len, unsigned long pgoff, 511 + unsigned long flags); 510 512 511 513 /** 512 514 * drm_gem_object_get - acquire a GEM buffer object reference