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