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