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
11struct drmm_node {
12	void *p;
13	size_t size;
14	drmm_func_t func;
15	struct list_head list;
16};
17
18void *
19drmm_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
39void *
40drmm_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
60char *
61drmm_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
81void
82drmm_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
105int
106drmm_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
121int
122drmm_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
139void
140drm_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
153void
154drmm_add_final_kfree(struct drm_device *dev, void *p)
155{
156	dev->managed.final_kfree = p;
157}
158