xref: /freebsd/sys/dev/smartpqi/smartpqi_tag.c (revision 61e21613)
1 /*-
2  * Copyright 2016-2023 Microchip Technology, Inc. and/or its subsidiaries.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 
27 #include "smartpqi_includes.h"
28 
29 #ifndef LOCKFREE_STACK
30 
31 /*
32  * Function used to release the tag from taglist.
33  */
34 void
35 pqisrc_put_tag(pqi_taglist_t *taglist, uint32_t elem)
36 {
37 
38 	OS_ACQUIRE_SPINLOCK(&(taglist->lock));
39 /*	DBG_FUNC("IN\n");*/
40 
41 	ASSERT(taglist->num_elem < taglist->max_elem);
42 
43 	if (taglist->num_elem < taglist->max_elem) {
44 		taglist->elem_array[taglist->tail] = elem;
45 		taglist->num_elem++;
46 		taglist->tail = (taglist->tail + 1) % taglist->max_elem;
47 	}
48 
49 	OS_RELEASE_SPINLOCK(&taglist->lock);
50 
51 /*	DBG_FUNC("OUT\n");*/
52 }
53 
54 /*
55  * Function used to get an unoccupied tag from the tag list.
56  */
57 uint32_t
58 pqisrc_get_tag(pqi_taglist_t *taglist)
59 {
60 	uint32_t elem = INVALID_ELEM;
61 
62 /*	DBG_FUNC("IN\n");*/
63 
64 	OS_ACQUIRE_SPINLOCK(&taglist->lock);
65 
66 	ASSERT(taglist->num_elem > 0);
67 
68 	if (taglist->num_elem > 0) {
69 		elem = taglist->elem_array[taglist->head];
70 		taglist->num_elem--;
71 		taglist->head = (taglist->head + 1) % taglist->max_elem;
72 	}
73 
74 	OS_RELEASE_SPINLOCK(&taglist->lock);
75 
76 /*	DBG_FUNC("OUT got %d\n", elem);*/
77 	return elem;
78 }
79 
80 /*
81  * Initialize circular queue implementation of tag list.
82  */
83 int
84 pqisrc_init_taglist(pqisrc_softstate_t *softs, pqi_taglist_t *taglist,
85 				uint32_t max_elem)
86 {
87 	int ret = PQI_STATUS_SUCCESS;
88 	int i = 0;
89 
90 	DBG_FUNC("IN\n");
91 
92 	taglist->max_elem = max_elem;
93 	taglist->num_elem = 0;
94 	taglist->head = 0;
95 	taglist->tail = 0;
96 	taglist->elem_array = os_mem_alloc(softs,
97 			(max_elem * sizeof(uint32_t)));
98 	if (!(taglist->elem_array)) {
99 		DBG_FUNC("Unable to allocate memory for taglist\n");
100 		ret = PQI_STATUS_FAILURE;
101 		goto err_out;
102 	}
103 
104     os_strlcpy(taglist->lockname, "tag_lock",  LOCKNAME_SIZE);
105     ret = os_init_spinlock(softs, &taglist->lock, taglist->lockname);
106     if(ret){
107         DBG_ERR("tag lock initialization failed\n");
108         taglist->lockcreated=false;
109         goto err_lock;
110 	}
111     taglist->lockcreated = true;
112 
113 	/* indices 1 to max_elem are considered as valid tags */
114 	for (i=1; i <= max_elem; i++) {
115 		softs->rcb[i].tag = INVALID_ELEM;
116 		pqisrc_put_tag(taglist, i);
117 	}
118 
119 	DBG_FUNC("OUT\n");
120 	return ret;
121 
122 err_lock:
123     os_mem_free(softs, (char *)taglist->elem_array,
124         (taglist->max_elem * sizeof(uint32_t)));
125 	taglist->elem_array = NULL;
126 err_out:
127 	DBG_FUNC("OUT failed\n");
128 	return ret;
129 }
130 
131 /*
132  * Destroy circular queue implementation of tag list.
133  */
134 void
135 pqisrc_destroy_taglist(pqisrc_softstate_t *softs, pqi_taglist_t *taglist)
136 {
137 	DBG_FUNC("IN\n");
138 	os_mem_free(softs, (char *)taglist->elem_array,
139 		(taglist->max_elem * sizeof(uint32_t)));
140 	taglist->elem_array = NULL;
141 
142     if(taglist->lockcreated==true){
143         os_uninit_spinlock(&taglist->lock);
144         taglist->lockcreated = false;
145     }
146 
147 	DBG_FUNC("OUT\n");
148 }
149 
150 #else	 /* LOCKFREE_STACK */
151 
152 /*
153  * Initialize circular queue implementation of tag list.
154  */
155 int
156 pqisrc_init_taglist(pqisrc_softstate_t *softs, lockless_stack_t *stack,
157 				uint32_t max_elem)
158 {
159 	int ret = PQI_STATUS_SUCCESS;
160 	int index = 0;
161 
162 	DBG_FUNC("IN\n");
163 
164 	/* indices 1 to max_elem are considered as valid tags */
165 	stack->max_elem = max_elem + 1;
166 	stack->head.data = 0;
167 	DBG_INFO("Stack head address :%p\n",&stack->head);
168 
169 	/*Allocate memory for stack*/
170 	stack->next_index_array = (uint32_t*)os_mem_alloc(softs,
171 		(stack->max_elem * sizeof(uint32_t)));
172 	if (!(stack->next_index_array)) {
173 		DBG_ERR("Unable to allocate memory for stack\n");
174 		ret = PQI_STATUS_FAILURE;
175 		goto err_out;
176 	}
177 
178 	/* push all the entries to the stack */
179 	for (index = 1; index < stack->max_elem ; index++) {
180 		softs->rcb[index].tag = INVALID_ELEM;
181 		pqisrc_put_tag(stack, index);
182 	}
183 
184 	DBG_FUNC("OUT\n");
185 	return ret;
186 err_out:
187 	DBG_FUNC("Failed OUT\n");
188 	return ret;
189 }
190 
191 /*
192  * Destroy circular queue implementation of tag list.
193  */
194 void
195 pqisrc_destroy_taglist(pqisrc_softstate_t *softs, lockless_stack_t *stack)
196 {
197 	DBG_FUNC("IN\n");
198 
199 	/* de-allocate stack memory */
200 	if (stack->next_index_array) {
201 		os_mem_free(softs,(char*)stack->next_index_array,
202 			(stack->max_elem * sizeof(uint32_t)));
203 		stack->next_index_array = NULL;
204 	}
205 
206 	DBG_FUNC("OUT\n");
207 }
208 
209 /*
210  * Function used to release the tag from taglist.
211  */
212 void
213 pqisrc_put_tag(lockless_stack_t *stack, uint32_t index)
214 {
215    union head_list cur_head, new_head;
216 
217 	DBG_FUNC("IN\n");
218  	DBG_INFO("push tag :%u\n",index);
219 
220 	if (index >= stack->max_elem) {
221 		ASSERT(false);
222 		DBG_INFO("Pushed Invalid index\n"); /* stack full */
223 		return;
224 	}
225 
226 	if (stack->next_index_array[index] != 0) {
227  		ASSERT(false);
228 		DBG_INFO("Index already present as tag in the stack\n");
229 		return;
230 	}
231 
232 	do {
233 		cur_head = stack->head;
234 		/* increment seq_no */
235  		new_head.top.seq_no = cur_head.top.seq_no + 1;
236 		/* update the index at the top of the stack with the new index */
237 		new_head.top.index = index;
238 		/* Create a link to the previous index */
239 		stack->next_index_array[index] = cur_head.top.index;
240 	}while(!os_atomic64_cas(&stack->head.data,cur_head.data,new_head.data));
241 	stack->num_elem++;
242  	DBG_FUNC("OUT\n");
243  	return;
244 }
245 
246 /*
247  * Function used to get an unoccupied tag from the tag list.
248  */
249 uint32_t
250 pqisrc_get_tag(lockless_stack_t *stack)
251 {
252 	union head_list cur_head, new_head;
253 
254 	DBG_FUNC("IN\n");
255 	do {
256 		cur_head = stack->head;
257 		if (cur_head.top.index == 0)    /* stack empty */
258 			return INVALID_ELEM;
259 		/* increment seq_no field */
260 		new_head.top.seq_no = cur_head.top.seq_no + 1;
261 		/* update the index at the top of the stack with the next index */
262 		new_head.top.index = stack->next_index_array[cur_head.top.index];
263 	}while(!os_atomic64_cas(&stack->head.data,cur_head.data,new_head.data));
264  	stack->next_index_array[cur_head.top.index] = 0;
265 	stack->num_elem--;
266 
267 	DBG_INFO("pop tag: %u\n",cur_head.top.index);
268  	DBG_FUNC("OUT\n");
269 	return cur_head.top.index; /*tag*/
270 }
271 #endif /* LOCKFREE_STACK */
272