linux/drivers/gpu/drm/i915/gem/i915_gem_create.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright © 2020 Intel Corporation
   4 */
   5
   6#include "gem/i915_gem_ioctls.h"
   7#include "gem/i915_gem_lmem.h"
   8#include "gem/i915_gem_region.h"
   9
  10#include "i915_drv.h"
  11#include "i915_trace.h"
  12#include "i915_user_extensions.h"
  13
  14static u32 object_max_page_size(struct intel_memory_region **placements,
  15                                unsigned int n_placements)
  16{
  17        u32 max_page_size = 0;
  18        int i;
  19
  20        for (i = 0; i < n_placements; i++) {
  21                struct intel_memory_region *mr = placements[i];
  22
  23                GEM_BUG_ON(!is_power_of_2(mr->min_page_size));
  24                max_page_size = max_t(u32, max_page_size, mr->min_page_size);
  25        }
  26
  27        GEM_BUG_ON(!max_page_size);
  28        return max_page_size;
  29}
  30
  31static int object_set_placements(struct drm_i915_gem_object *obj,
  32                                 struct intel_memory_region **placements,
  33                                 unsigned int n_placements)
  34{
  35        struct intel_memory_region **arr;
  36        unsigned int i;
  37
  38        GEM_BUG_ON(!n_placements);
  39
  40        /*
  41         * For the common case of one memory region, skip storing an
  42         * allocated array and just point at the region directly.
  43         */
  44        if (n_placements == 1) {
  45                struct intel_memory_region *mr = placements[0];
  46                struct drm_i915_private *i915 = mr->i915;
  47
  48                obj->mm.placements = &i915->mm.regions[mr->id];
  49                obj->mm.n_placements = 1;
  50        } else {
  51                arr = kmalloc_array(n_placements,
  52                                    sizeof(struct intel_memory_region *),
  53                                    GFP_KERNEL);
  54                if (!arr)
  55                        return -ENOMEM;
  56
  57                for (i = 0; i < n_placements; i++)
  58                        arr[i] = placements[i];
  59
  60                obj->mm.placements = arr;
  61                obj->mm.n_placements = n_placements;
  62        }
  63
  64        return 0;
  65}
  66
  67static int i915_gem_publish(struct drm_i915_gem_object *obj,
  68                            struct drm_file *file,
  69                            u64 *size_p,
  70                            u32 *handle_p)
  71{
  72        u64 size = obj->base.size;
  73        int ret;
  74
  75        ret = drm_gem_handle_create(file, &obj->base, handle_p);
  76        /* drop reference from allocate - handle holds it now */
  77        i915_gem_object_put(obj);
  78        if (ret)
  79                return ret;
  80
  81        *size_p = size;
  82        return 0;
  83}
  84
  85/**
  86 * Creates a new object using the same path as DRM_I915_GEM_CREATE_EXT
  87 * @i915: i915 private
  88 * @size: size of the buffer, in bytes
  89 * @placements: possible placement regions, in priority order
  90 * @n_placements: number of possible placement regions
  91 *
  92 * This function is exposed primarily for selftests and does very little
  93 * error checking.  It is assumed that the set of placement regions has
  94 * already been verified to be valid.
  95 */
  96struct drm_i915_gem_object *
  97__i915_gem_object_create_user(struct drm_i915_private *i915, u64 size,
  98                              struct intel_memory_region **placements,
  99                              unsigned int n_placements)
 100{
 101        struct intel_memory_region *mr = placements[0];
 102        struct drm_i915_gem_object *obj;
 103        unsigned int flags;
 104        int ret;
 105
 106        i915_gem_flush_free_objects(i915);
 107
 108        size = round_up(size, object_max_page_size(placements, n_placements));
 109        if (size == 0)
 110                return ERR_PTR(-EINVAL);
 111
 112        /* For most of the ABI (e.g. mmap) we think in system pages */
 113        GEM_BUG_ON(!IS_ALIGNED(size, PAGE_SIZE));
 114
 115        if (i915_gem_object_size_2big(size))
 116                return ERR_PTR(-E2BIG);
 117
 118        obj = i915_gem_object_alloc();
 119        if (!obj)
 120                return ERR_PTR(-ENOMEM);
 121
 122        ret = object_set_placements(obj, placements, n_placements);
 123        if (ret)
 124                goto object_free;
 125
 126        /*
 127         * I915_BO_ALLOC_USER will make sure the object is cleared before
 128         * any user access.
 129         */
 130        flags = I915_BO_ALLOC_USER;
 131
 132        ret = mr->ops->init_object(mr, obj, size, 0, flags);
 133        if (ret)
 134                goto object_free;
 135
 136        GEM_BUG_ON(size != obj->base.size);
 137
 138        trace_i915_gem_object_create(obj);
 139        return obj;
 140
 141object_free:
 142        if (obj->mm.n_placements > 1)
 143                kfree(obj->mm.placements);
 144        i915_gem_object_free(obj);
 145        return ERR_PTR(ret);
 146}
 147
 148int
 149i915_gem_dumb_create(struct drm_file *file,
 150                     struct drm_device *dev,
 151                     struct drm_mode_create_dumb *args)
 152{
 153        struct drm_i915_gem_object *obj;
 154        struct intel_memory_region *mr;
 155        enum intel_memory_type mem_type;
 156        int cpp = DIV_ROUND_UP(args->bpp, 8);
 157        u32 format;
 158
 159        switch (cpp) {
 160        case 1:
 161                format = DRM_FORMAT_C8;
 162                break;
 163        case 2:
 164                format = DRM_FORMAT_RGB565;
 165                break;
 166        case 4:
 167                format = DRM_FORMAT_XRGB8888;
 168                break;
 169        default:
 170                return -EINVAL;
 171        }
 172
 173        /* have to work out size/pitch and return them */
 174        args->pitch = ALIGN(args->width * cpp, 64);
 175
 176        /* align stride to page size so that we can remap */
 177        if (args->pitch > intel_plane_fb_max_stride(to_i915(dev), format,
 178                                                    DRM_FORMAT_MOD_LINEAR))
 179                args->pitch = ALIGN(args->pitch, 4096);
 180
 181        if (args->pitch < args->width)
 182                return -EINVAL;
 183
 184        args->size = mul_u32_u32(args->pitch, args->height);
 185
 186        mem_type = INTEL_MEMORY_SYSTEM;
 187        if (HAS_LMEM(to_i915(dev)))
 188                mem_type = INTEL_MEMORY_LOCAL;
 189
 190        mr = intel_memory_region_by_type(to_i915(dev), mem_type);
 191
 192        obj = __i915_gem_object_create_user(to_i915(dev), args->size, &mr, 1);
 193        if (IS_ERR(obj))
 194                return PTR_ERR(obj);
 195
 196        return i915_gem_publish(obj, file, &args->size, &args->handle);
 197}
 198
 199/**
 200 * Creates a new mm object and returns a handle to it.
 201 * @dev: drm device pointer
 202 * @data: ioctl data blob
 203 * @file: drm file pointer
 204 */
 205int
 206i915_gem_create_ioctl(struct drm_device *dev, void *data,
 207                      struct drm_file *file)
 208{
 209        struct drm_i915_private *i915 = to_i915(dev);
 210        struct drm_i915_gem_create *args = data;
 211        struct drm_i915_gem_object *obj;
 212        struct intel_memory_region *mr;
 213
 214        mr = intel_memory_region_by_type(i915, INTEL_MEMORY_SYSTEM);
 215
 216        obj = __i915_gem_object_create_user(i915, args->size, &mr, 1);
 217        if (IS_ERR(obj))
 218                return PTR_ERR(obj);
 219
 220        return i915_gem_publish(obj, file, &args->size, &args->handle);
 221}
 222
 223struct create_ext {
 224        struct drm_i915_private *i915;
 225        struct intel_memory_region *placements[INTEL_REGION_UNKNOWN];
 226        unsigned int n_placements;
 227};
 228
 229static void repr_placements(char *buf, size_t size,
 230                            struct intel_memory_region **placements,
 231                            int n_placements)
 232{
 233        int i;
 234
 235        buf[0] = '\0';
 236
 237        for (i = 0; i < n_placements; i++) {
 238                struct intel_memory_region *mr = placements[i];
 239                int r;
 240
 241                r = snprintf(buf, size, "\n  %s -> { class: %d, inst: %d }",
 242                             mr->name, mr->type, mr->instance);
 243                if (r >= size)
 244                        return;
 245
 246                buf += r;
 247                size -= r;
 248        }
 249}
 250
 251static int set_placements(struct drm_i915_gem_create_ext_memory_regions *args,
 252                          struct create_ext *ext_data)
 253{
 254        struct drm_i915_private *i915 = ext_data->i915;
 255        struct drm_i915_gem_memory_class_instance __user *uregions =
 256                u64_to_user_ptr(args->regions);
 257        struct intel_memory_region *placements[INTEL_REGION_UNKNOWN];
 258        u32 mask;
 259        int i, ret = 0;
 260
 261        if (args->pad) {
 262                drm_dbg(&i915->drm, "pad should be zero\n");
 263                ret = -EINVAL;
 264        }
 265
 266        if (!args->num_regions) {
 267                drm_dbg(&i915->drm, "num_regions is zero\n");
 268                ret = -EINVAL;
 269        }
 270
 271        BUILD_BUG_ON(ARRAY_SIZE(i915->mm.regions) != ARRAY_SIZE(placements));
 272        BUILD_BUG_ON(ARRAY_SIZE(ext_data->placements) != ARRAY_SIZE(placements));
 273        if (args->num_regions > ARRAY_SIZE(i915->mm.regions)) {
 274                drm_dbg(&i915->drm, "num_regions is too large\n");
 275                ret = -EINVAL;
 276        }
 277
 278        if (ret)
 279                return ret;
 280
 281        mask = 0;
 282        for (i = 0; i < args->num_regions; i++) {
 283                struct drm_i915_gem_memory_class_instance region;
 284                struct intel_memory_region *mr;
 285
 286                if (copy_from_user(&region, uregions, sizeof(region)))
 287                        return -EFAULT;
 288
 289                mr = intel_memory_region_lookup(i915,
 290                                                region.memory_class,
 291                                                region.memory_instance);
 292                if (!mr || mr->private) {
 293                        drm_dbg(&i915->drm, "Device is missing region { class: %d, inst: %d } at index = %d\n",
 294                                region.memory_class, region.memory_instance, i);
 295                        ret = -EINVAL;
 296                        goto out_dump;
 297                }
 298
 299                if (mask & BIT(mr->id)) {
 300                        drm_dbg(&i915->drm, "Found duplicate placement %s -> { class: %d, inst: %d } at index = %d\n",
 301                                mr->name, region.memory_class,
 302                                region.memory_instance, i);
 303                        ret = -EINVAL;
 304                        goto out_dump;
 305                }
 306
 307                placements[i] = mr;
 308                mask |= BIT(mr->id);
 309
 310                ++uregions;
 311        }
 312
 313        if (ext_data->n_placements) {
 314                ret = -EINVAL;
 315                goto out_dump;
 316        }
 317
 318        ext_data->n_placements = args->num_regions;
 319        for (i = 0; i < args->num_regions; i++)
 320                ext_data->placements[i] = placements[i];
 321
 322        return 0;
 323
 324out_dump:
 325        if (1) {
 326                char buf[256];
 327
 328                if (ext_data->n_placements) {
 329                        repr_placements(buf,
 330                                        sizeof(buf),
 331                                        ext_data->placements,
 332                                        ext_data->n_placements);
 333                        drm_dbg(&i915->drm,
 334                                "Placements were already set in previous EXT. Existing placements: %s\n",
 335                                buf);
 336                }
 337
 338                repr_placements(buf, sizeof(buf), placements, i);
 339                drm_dbg(&i915->drm, "New placements(so far validated): %s\n", buf);
 340        }
 341
 342        return ret;
 343}
 344
 345static int ext_set_placements(struct i915_user_extension __user *base,
 346                              void *data)
 347{
 348        struct drm_i915_gem_create_ext_memory_regions ext;
 349
 350        if (!IS_ENABLED(CONFIG_DRM_I915_UNSTABLE_FAKE_LMEM))
 351                return -ENODEV;
 352
 353        if (copy_from_user(&ext, base, sizeof(ext)))
 354                return -EFAULT;
 355
 356        return set_placements(&ext, data);
 357}
 358
 359static const i915_user_extension_fn create_extensions[] = {
 360        [I915_GEM_CREATE_EXT_MEMORY_REGIONS] = ext_set_placements,
 361};
 362
 363/**
 364 * Creates a new mm object and returns a handle to it.
 365 * @dev: drm device pointer
 366 * @data: ioctl data blob
 367 * @file: drm file pointer
 368 */
 369int
 370i915_gem_create_ext_ioctl(struct drm_device *dev, void *data,
 371                          struct drm_file *file)
 372{
 373        struct drm_i915_private *i915 = to_i915(dev);
 374        struct drm_i915_gem_create_ext *args = data;
 375        struct create_ext ext_data = { .i915 = i915 };
 376        struct drm_i915_gem_object *obj;
 377        int ret;
 378
 379        if (args->flags)
 380                return -EINVAL;
 381
 382        ret = i915_user_extensions(u64_to_user_ptr(args->extensions),
 383                                   create_extensions,
 384                                   ARRAY_SIZE(create_extensions),
 385                                   &ext_data);
 386        if (ret)
 387                return ret;
 388
 389        if (!ext_data.n_placements) {
 390                ext_data.placements[0] =
 391                        intel_memory_region_by_type(i915, INTEL_MEMORY_SYSTEM);
 392                ext_data.n_placements = 1;
 393        }
 394
 395        obj = __i915_gem_object_create_user(i915, args->size,
 396                                            ext_data.placements,
 397                                            ext_data.n_placements);
 398        if (IS_ERR(obj))
 399                return PTR_ERR(obj);
 400
 401        return i915_gem_publish(obj, file, &args->size, &args->handle);
 402}
 403