Linux-libre 4.19.116-gnu
[librecmc/linux-libre.git] / drivers / gpu / drm / vkms / vkms_gem.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <linux/shmem_fs.h>
4
5 #include "vkms_drv.h"
6
7 static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev,
8                                                  u64 size)
9 {
10         struct vkms_gem_object *obj;
11         int ret;
12
13         obj = kzalloc(sizeof(*obj), GFP_KERNEL);
14         if (!obj)
15                 return ERR_PTR(-ENOMEM);
16
17         size = roundup(size, PAGE_SIZE);
18         ret = drm_gem_object_init(dev, &obj->gem, size);
19         if (ret) {
20                 kfree(obj);
21                 return ERR_PTR(ret);
22         }
23
24         mutex_init(&obj->pages_lock);
25
26         return obj;
27 }
28
29 void vkms_gem_free_object(struct drm_gem_object *obj)
30 {
31         struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object,
32                                                    gem);
33
34         kvfree(gem->pages);
35         mutex_destroy(&gem->pages_lock);
36         drm_gem_object_release(obj);
37         kfree(gem);
38 }
39
40 int vkms_gem_fault(struct vm_fault *vmf)
41 {
42         struct vm_area_struct *vma = vmf->vma;
43         struct vkms_gem_object *obj = vma->vm_private_data;
44         unsigned long vaddr = vmf->address;
45         pgoff_t page_offset;
46         loff_t num_pages;
47         int ret;
48
49         page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
50         num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE);
51
52         if (page_offset > num_pages)
53                 return VM_FAULT_SIGBUS;
54
55         ret = -ENOENT;
56         mutex_lock(&obj->pages_lock);
57         if (obj->pages) {
58                 get_page(obj->pages[page_offset]);
59                 vmf->page = obj->pages[page_offset];
60                 ret = 0;
61         }
62         mutex_unlock(&obj->pages_lock);
63         if (ret) {
64                 struct page *page;
65                 struct address_space *mapping;
66
67                 mapping = file_inode(obj->gem.filp)->i_mapping;
68                 page = shmem_read_mapping_page(mapping, page_offset);
69
70                 if (!IS_ERR(page)) {
71                         vmf->page = page;
72                         ret = 0;
73                 } else {
74                         switch (PTR_ERR(page)) {
75                         case -ENOSPC:
76                         case -ENOMEM:
77                                 ret = VM_FAULT_OOM;
78                                 break;
79                         case -EBUSY:
80                                 ret = VM_FAULT_RETRY;
81                                 break;
82                         case -EFAULT:
83                         case -EINVAL:
84                                 ret = VM_FAULT_SIGBUS;
85                                 break;
86                         default:
87                                 WARN_ON(PTR_ERR(page));
88                                 ret = VM_FAULT_SIGBUS;
89                                 break;
90                         }
91                 }
92         }
93         return ret;
94 }
95
96 struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
97                                        struct drm_file *file,
98                                        u32 *handle,
99                                        u64 size)
100 {
101         struct vkms_gem_object *obj;
102         int ret;
103
104         if (!file || !dev || !handle)
105                 return ERR_PTR(-EINVAL);
106
107         obj = __vkms_gem_create(dev, size);
108         if (IS_ERR(obj))
109                 return ERR_CAST(obj);
110
111         ret = drm_gem_handle_create(file, &obj->gem, handle);
112         drm_gem_object_put_unlocked(&obj->gem);
113         if (ret)
114                 return ERR_PTR(ret);
115
116         return &obj->gem;
117 }
118
119 int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
120                      struct drm_mode_create_dumb *args)
121 {
122         struct drm_gem_object *gem_obj;
123         u64 pitch, size;
124
125         if (!args || !dev || !file)
126                 return -EINVAL;
127
128         pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
129         size = pitch * args->height;
130
131         if (!size)
132                 return -EINVAL;
133
134         gem_obj = vkms_gem_create(dev, file, &args->handle, size);
135         if (IS_ERR(gem_obj))
136                 return PTR_ERR(gem_obj);
137
138         args->size = gem_obj->size;
139         args->pitch = pitch;
140
141         DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
142
143         return 0;
144 }
145
146 int vkms_dumb_map(struct drm_file *file, struct drm_device *dev,
147                   u32 handle, u64 *offset)
148 {
149         struct drm_gem_object *obj;
150         int ret;
151
152         obj = drm_gem_object_lookup(file, handle);
153         if (!obj)
154                 return -ENOENT;
155
156         if (!obj->filp) {
157                 ret = -EINVAL;
158                 goto unref;
159         }
160
161         ret = drm_gem_create_mmap_offset(obj);
162         if (ret)
163                 goto unref;
164
165         *offset = drm_vma_node_offset_addr(&obj->vma_node);
166 unref:
167         drm_gem_object_put_unlocked(obj);
168
169         return ret;
170 }