1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Support for Intel Camera Imaging ISP subsystem.
4  * Copyright (c) 2015, Intel Corporation.
5  */
6 
7 #include "hmm.h"
8 
9 #include "ia_css_refcount.h"
10 #include "sh_css_defs.h"
11 
12 #include "platform_support.h"
13 
14 #include "assert_support.h"
15 
16 #include "ia_css_debug.h"
17 
18 /* TODO: enable for other memory aswell
19 	 now only for ia_css_ptr */
20 struct ia_css_refcount_entry {
21 	u32 count;
22 	ia_css_ptr data;
23 	s32 id;
24 };
25 
26 struct ia_css_refcount_list {
27 	u32 size;
28 	struct ia_css_refcount_entry *items;
29 };
30 
31 static struct ia_css_refcount_list myrefcount;
32 
refcount_find_entry(ia_css_ptr ptr,bool firstfree)33 static struct ia_css_refcount_entry *refcount_find_entry(ia_css_ptr ptr,
34 	bool firstfree)
35 {
36 	u32 i;
37 
38 	if (ptr == 0)
39 		return NULL;
40 	if (!myrefcount.items) {
41 		ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
42 				    "%s(): Ref count not initialized!\n", __func__);
43 		return NULL;
44 	}
45 
46 	for (i = 0; i < myrefcount.size; i++) {
47 		if ((&myrefcount.items[i])->data == 0) {
48 			if (firstfree) {
49 				/* for new entry */
50 				return &myrefcount.items[i];
51 			}
52 		}
53 		if ((&myrefcount.items[i])->data == ptr) {
54 			/* found entry */
55 			return &myrefcount.items[i];
56 		}
57 	}
58 	return NULL;
59 }
60 
ia_css_refcount_init(uint32_t size)61 int ia_css_refcount_init(uint32_t size)
62 {
63 	int err = 0;
64 
65 	if (size == 0) {
66 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
67 				    "%s(): Size of 0 for Ref count init!\n", __func__);
68 		return -EINVAL;
69 	}
70 	if (myrefcount.items) {
71 		ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
72 				    "%s(): Ref count is already initialized\n", __func__);
73 		return -EINVAL;
74 	}
75 	myrefcount.items =
76 	    kvmalloc(sizeof(struct ia_css_refcount_entry) * size, GFP_KERNEL);
77 	if (!myrefcount.items)
78 		err = -ENOMEM;
79 	if (!err) {
80 		memset(myrefcount.items, 0,
81 		       sizeof(struct ia_css_refcount_entry) * size);
82 		myrefcount.size = size;
83 	}
84 	return err;
85 }
86 
ia_css_refcount_uninit(void)87 void ia_css_refcount_uninit(void)
88 {
89 	struct ia_css_refcount_entry *entry;
90 	u32 i;
91 
92 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
93 			    "%s() entry\n", __func__);
94 	for (i = 0; i < myrefcount.size; i++) {
95 		/* driver verifier tool has issues with &arr[i]
96 		   and prefers arr + i; as these are actually equivalent
97 		   the line below uses + i
98 		*/
99 		entry = myrefcount.items + i;
100 		if (entry->data != mmgr_NULL) {
101 			/*	ia_css_debug_dtrace(IA_CSS_DBG_TRACE,
102 				"ia_css_refcount_uninit: freeing (%x)\n",
103 				entry->data);*/
104 			hmm_free(entry->data);
105 			entry->data = mmgr_NULL;
106 			entry->count = 0;
107 			entry->id = 0;
108 		}
109 	}
110 	kvfree(myrefcount.items);
111 	myrefcount.items = NULL;
112 	myrefcount.size = 0;
113 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
114 			    "%s() leave\n", __func__);
115 }
116 
ia_css_refcount_increment(s32 id,ia_css_ptr ptr)117 ia_css_ptr ia_css_refcount_increment(s32 id, ia_css_ptr ptr)
118 {
119 	struct ia_css_refcount_entry *entry;
120 
121 	if (ptr == mmgr_NULL)
122 		return ptr;
123 
124 	entry = refcount_find_entry(ptr, false);
125 
126 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
127 			    "%s(%x) 0x%x\n", __func__, id, ptr);
128 
129 	if (!entry) {
130 		entry = refcount_find_entry(ptr, true);
131 		assert(entry);
132 		if (!entry)
133 			return mmgr_NULL;
134 		entry->id = id;
135 	}
136 
137 	if (entry->id != id) {
138 		ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
139 				    "%s(): Ref count IDS do not match!\n", __func__);
140 		return mmgr_NULL;
141 	}
142 
143 	if (entry->data == ptr)
144 		entry->count += 1;
145 	else if (entry->data == mmgr_NULL) {
146 		entry->data = ptr;
147 		entry->count = 1;
148 	} else
149 		return mmgr_NULL;
150 
151 	return ptr;
152 }
153 
ia_css_refcount_decrement(s32 id,ia_css_ptr ptr)154 bool ia_css_refcount_decrement(s32 id, ia_css_ptr ptr)
155 {
156 	struct ia_css_refcount_entry *entry;
157 
158 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
159 			    "%s(%x) 0x%x\n", __func__, id, ptr);
160 
161 	if (ptr == mmgr_NULL)
162 		return false;
163 
164 	entry = refcount_find_entry(ptr, false);
165 
166 	if (entry) {
167 		if (entry->id != id) {
168 			ia_css_debug_dtrace(IA_CSS_DEBUG_ERROR,
169 					    "%s(): Ref count IDS do not match!\n", __func__);
170 			return false;
171 		}
172 		if (entry->count > 0) {
173 			entry->count -= 1;
174 			if (entry->count == 0) {
175 				/* ia_css_debug_dtrace(IA_CSS_DBEUG_TRACE,
176 				   "ia_css_refcount_decrement: freeing\n");*/
177 				hmm_free(ptr);
178 				entry->data = mmgr_NULL;
179 				entry->id = 0;
180 			}
181 			return true;
182 		}
183 	}
184 
185 	/* SHOULD NOT HAPPEN: ptr not managed by refcount, or not
186 	   valid anymore */
187 	if (entry)
188 		IA_CSS_ERROR("id %x, ptr 0x%x entry %p entry->id %x entry->count %d\n",
189 			     id, ptr, entry, entry->id, entry->count);
190 	else
191 		IA_CSS_ERROR("entry NULL\n");
192 	assert(false);
193 
194 	return false;
195 }
196 
ia_css_refcount_is_single(ia_css_ptr ptr)197 bool ia_css_refcount_is_single(ia_css_ptr ptr)
198 {
199 	struct ia_css_refcount_entry *entry;
200 
201 	if (ptr == mmgr_NULL)
202 		return false;
203 
204 	entry = refcount_find_entry(ptr, false);
205 
206 	if (entry)
207 		return (entry->count == 1);
208 
209 	return true;
210 }
211 
ia_css_refcount_clear(s32 id,clear_func clear_func_ptr)212 void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr)
213 {
214 	struct ia_css_refcount_entry *entry;
215 	u32 i;
216 	u32 count = 0;
217 
218 	assert(clear_func_ptr);
219 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "%s(%x)\n",
220 			    __func__, id);
221 
222 	for (i = 0; i < myrefcount.size; i++) {
223 		/* driver verifier tool has issues with &arr[i]
224 		   and prefers arr + i; as these are actually equivalent
225 		   the line below uses + i
226 		*/
227 		entry = myrefcount.items + i;
228 		if ((entry->data != mmgr_NULL) && (entry->id == id)) {
229 			ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
230 					    "%s: %x: 0x%x\n", __func__,
231 					    id, entry->data);
232 			if (clear_func_ptr) {
233 				/* clear using provided function */
234 				clear_func_ptr(entry->data);
235 			} else {
236 				ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
237 						    "%s: using hmm_free: no clear_func\n", __func__);
238 				hmm_free(entry->data);
239 			}
240 
241 			if (entry->count != 0) {
242 				IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id);
243 			}
244 
245 			assert(entry->count == 0);
246 
247 			entry->data = mmgr_NULL;
248 			entry->count = 0;
249 			entry->id = 0;
250 			count++;
251 		}
252 	}
253 	ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
254 			    "%s(%x): cleared %d\n", __func__, id,
255 			    count);
256 }
257 
ia_css_refcount_is_valid(ia_css_ptr ptr)258 bool ia_css_refcount_is_valid(ia_css_ptr ptr)
259 {
260 	struct ia_css_refcount_entry *entry;
261 
262 	if (ptr == mmgr_NULL)
263 		return false;
264 
265 	entry = refcount_find_entry(ptr, false);
266 
267 	return entry;
268 }
269