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