1 /* Public domain. */
2
3 #include <linux/kernel.h>
4 #include <linux/slab.h>
5 #include <linux/string.h>
6 #include <linux/list.h>
7
8 #include <drm/drm_device.h>
9 #include <drm/drm_managed.h>
10
11 struct drmm_node {
12 void *p;
13 size_t size;
14 drmm_func_t func;
15 struct list_head list;
16 };
17
18 void *
drmm_kzalloc(struct drm_device * dev,size_t size,int flags)19 drmm_kzalloc(struct drm_device *dev, size_t size, int flags)
20 {
21 void *p;
22 struct drmm_node *node = malloc(sizeof(*node), M_DRM, flags | M_ZERO);
23 if (node == NULL)
24 return NULL;
25 p = kzalloc(size, flags);
26 if (p == NULL) {
27 free(node, M_DRM, sizeof(*node));
28 return NULL;
29 }
30 INIT_LIST_HEAD(&node->list);
31 node->p = p;
32 node->size = size;
33 mtx_enter(&dev->managed.lock);
34 list_add(&node->list, &dev->managed.resources);
35 mtx_leave(&dev->managed.lock);
36 return p;
37 }
38
39 void *
drmm_kcalloc(struct drm_device * dev,size_t n,size_t size,int flags)40 drmm_kcalloc(struct drm_device *dev, size_t n, size_t size, int flags)
41 {
42 void *p;
43 struct drmm_node *node = malloc(sizeof(*node), M_DRM, flags | M_ZERO);
44 if (node == NULL)
45 return NULL;
46 p = kcalloc(n, size, flags);
47 if (p == NULL) {
48 free(node, M_DRM, sizeof(*node));
49 return NULL;
50 }
51 INIT_LIST_HEAD(&node->list);
52 node->p = p;
53 node->size = n * size;
54 mtx_enter(&dev->managed.lock);
55 list_add(&node->list, &dev->managed.resources);
56 mtx_leave(&dev->managed.lock);
57 return p;
58 }
59
60 char *
drmm_kstrdup(struct drm_device * dev,const char * s,int flags)61 drmm_kstrdup(struct drm_device *dev, const char *s, int flags)
62 {
63 char *p;
64 struct drmm_node *node = malloc(sizeof(*node), M_DRM, flags | M_ZERO);
65 if (node == NULL)
66 return NULL;
67 p = kstrdup(s, flags);
68 if (p == NULL) {
69 free(node, M_DRM, sizeof(*node));
70 return NULL;
71 }
72 INIT_LIST_HEAD(&node->list);
73 node->p = p;
74 node->size = strlen(s) + 1;
75 mtx_enter(&dev->managed.lock);
76 list_add(&node->list, &dev->managed.resources);
77 mtx_leave(&dev->managed.lock);
78 return p;
79 }
80
81 void
drmm_kfree(struct drm_device * dev,void * p)82 drmm_kfree(struct drm_device *dev, void *p)
83 {
84 struct drmm_node *n, *m = NULL;
85
86 if (p == NULL)
87 return;
88
89 mtx_enter(&dev->managed.lock);
90 list_for_each_entry(n, &dev->managed.resources, list) {
91 if (n->p == p) {
92 list_del(&n->list);
93 m = n;
94 break;
95 }
96 }
97 mtx_leave(&dev->managed.lock);
98
99 if (m != NULL) {
100 free(m->p, M_DRM, m->size);
101 free(m, M_DRM, sizeof(*m));
102 }
103 }
104
105 int
drmm_add_action(struct drm_device * dev,drmm_func_t f,void * cookie)106 drmm_add_action(struct drm_device *dev, drmm_func_t f, void *cookie)
107 {
108 struct drmm_node *node = malloc(sizeof(*node), M_DRM, M_WAITOK | M_ZERO);
109 if (node == NULL)
110 return -ENOMEM;
111 INIT_LIST_HEAD(&node->list);
112 node->func = f;
113 node->p = cookie;
114 mtx_enter(&dev->managed.lock);
115 list_add(&node->list, &dev->managed.resources);
116 mtx_leave(&dev->managed.lock);
117
118 return 0;
119 }
120
121 int
drmm_add_action_or_reset(struct drm_device * dev,drmm_func_t f,void * cookie)122 drmm_add_action_or_reset(struct drm_device *dev, drmm_func_t f, void *cookie)
123 {
124 struct drmm_node *node = malloc(sizeof(*node), M_DRM, M_WAITOK | M_ZERO);
125 if (node == NULL) {
126 f(dev, cookie);
127 return -ENOMEM;
128 }
129 INIT_LIST_HEAD(&node->list);
130 node->func = f;
131 node->p = cookie;
132 mtx_enter(&dev->managed.lock);
133 list_add(&node->list, &dev->managed.resources);
134 mtx_leave(&dev->managed.lock);
135
136 return 0;
137 }
138
139 void
drm_managed_release(struct drm_device * dev)140 drm_managed_release(struct drm_device *dev)
141 {
142 struct drmm_node *n, *t;
143 list_for_each_entry_safe(n, t, &dev->managed.resources, list) {
144 list_del(&n->list);
145 if (n->func)
146 n->func(dev, n->p);
147 else
148 free(n->p, M_DRM, n->size);
149 free(n, M_DRM, sizeof(*n));
150 }
151 }
152
153 void
drmm_add_final_kfree(struct drm_device * dev,void * p)154 drmm_add_final_kfree(struct drm_device *dev, void *p)
155 {
156 dev->managed.final_kfree = p;
157 }
158