xref: /freebsd/sys/dev/ocs_fc/ocs_io.c (revision 95ee2897)
1 /*-
2  * Copyright (c) 2017 Broadcom. All rights reserved.
3  * The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /**
33  * @file
34  * Provide IO object allocation.
35  */
36 
37 /*!
38  * @defgroup io_alloc IO allocation
39  */
40 
41 #include "ocs.h"
42 #include "ocs_scsi.h"
43 #include "ocs_els.h"
44 #include "ocs_utils.h"
45 
46 void ocs_mgmt_io_list(ocs_textbuf_t *textbuf, void *io);
47 void ocs_mgmt_io_get_all(ocs_textbuf_t *textbuf, void *io);
48 int ocs_mgmt_io_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *io);
49 
50 static ocs_mgmt_functions_t io_mgmt_functions = {
51 	.get_list_handler	=	ocs_mgmt_io_list,
52 	.get_handler		=	ocs_mgmt_io_get,
53 	.get_all_handler	=	ocs_mgmt_io_get_all,
54 };
55 
56 /**
57  * @brief IO pool.
58  *
59  * Structure encapsulating a pool of IO objects.
60  *
61  */
62 
63 struct ocs_io_pool_s {
64 	ocs_t *ocs;			/* Pointer to device object */
65 	ocs_lock_t lock;		/* IO pool lock */
66 	uint32_t io_num_ios;		/* Total IOs allocated */
67 	ocs_pool_t *pool;
68 };
69 
70 /**
71  * @brief Create a pool of IO objects.
72  *
73  * @par Description
74  * This function allocates memory in larger chucks called
75  * "slabs" which are a fixed size. It calculates the number of IO objects that
76  * fit within each "slab" and determines the number of "slabs" required to
77  * allocate the number of IOs requested. Each of the slabs is allocated and
78  * then it grabs each IO object within the slab and adds it to the free list.
79  * Individual command, response and SGL DMA buffers are allocated for each IO.
80  *
81  *           "Slabs"
82  *      +----------------+
83  *      |                |
84  *   +----------------+  |
85  *   |    IO          |  |
86  *   +----------------+  |
87  *   |    ...         |  |
88  *   +----------------+__+
89  *   |    IO          |
90  *   +----------------+
91  *
92  * @param ocs Driver instance's software context.
93  * @param num_io Number of IO contexts to allocate.
94  * @param num_sgl Number of SGL entries to allocate for each IO.
95  *
96  * @return Returns a pointer to a new ocs_io_pool_t on success,
97  *         or NULL on failure.
98  */
99 
100 ocs_io_pool_t *
ocs_io_pool_create(ocs_t * ocs,uint32_t num_io,uint32_t num_sgl)101 ocs_io_pool_create(ocs_t *ocs, uint32_t num_io, uint32_t num_sgl)
102 {
103 	uint32_t i = 0;
104 	int32_t	rc = -1;
105 	ocs_io_pool_t *io_pool;
106 
107 	/* Allocate the IO pool */
108 	io_pool = ocs_malloc(ocs, sizeof(*io_pool), OCS_M_ZERO | OCS_M_NOWAIT);
109 	if (io_pool == NULL) {
110 		ocs_log_err(ocs, "allocate of IO pool failed\n");
111 		return NULL;
112 	}
113 
114 	io_pool->ocs = ocs;
115 	io_pool->io_num_ios = num_io;
116 
117 	/* initialize IO pool lock */
118 	ocs_lock_init(ocs, &io_pool->lock, "io_pool lock[%d]", ocs->instance_index);
119 
120 	io_pool->pool = ocs_pool_alloc(ocs, sizeof(ocs_io_t), io_pool->io_num_ios, FALSE);
121 
122 	for (i = 0; i < io_pool->io_num_ios; i++) {
123 		ocs_io_t *io = ocs_pool_get_instance(io_pool->pool, i);
124 
125 		io->tag = i;
126 		io->instance_index = i;
127 		io->ocs = ocs;
128 
129 		/* allocate a command/response dma buffer */
130 		if (ocs->enable_ini) {
131 			rc = ocs_dma_alloc(ocs, &io->cmdbuf, SCSI_CMD_BUF_LENGTH, OCS_MIN_DMA_ALIGNMENT);
132 			if (rc) {
133 				ocs_log_err(ocs, "ocs_dma_alloc cmdbuf failed\n");
134 				ocs_io_pool_free(io_pool);
135 				return NULL;
136 			}
137 		}
138 
139 		/* Allocate a response buffer */
140 		rc = ocs_dma_alloc(ocs, &io->rspbuf, SCSI_RSP_BUF_LENGTH, OCS_MIN_DMA_ALIGNMENT);
141 		if (rc) {
142 			ocs_log_err(ocs, "ocs_dma_alloc cmdbuf failed\n");
143 			ocs_io_pool_free(io_pool);
144 			return NULL;
145 		}
146 
147 		/* Allocate SGL */
148 		io->sgl = ocs_malloc(ocs, sizeof(*io->sgl) * num_sgl, OCS_M_NOWAIT | OCS_M_ZERO);
149 		if (io->sgl == NULL) {
150 			ocs_log_err(ocs, "malloc sgl's failed\n");
151 			ocs_io_pool_free(io_pool);
152 			return NULL;
153 		}
154 		io->sgl_allocated = num_sgl;
155 		io->sgl_count = 0;
156 
157 		/* Make IO backend call to initialize IO */
158 		ocs_scsi_tgt_io_init(io);
159 		ocs_scsi_ini_io_init(io);
160 
161 		rc = ocs_dma_alloc(ocs, &io->els_req, OCS_ELS_REQ_LEN, OCS_MIN_DMA_ALIGNMENT);
162 		if (rc) {
163 			ocs_log_err(ocs, "ocs_dma_alloc els_req failed\n");
164 			ocs_io_pool_free(io_pool);
165 			return NULL;
166  		}
167 
168 		rc = ocs_dma_alloc(ocs, &io->els_rsp, OCS_ELS_GID_PT_RSP_LEN, OCS_MIN_DMA_ALIGNMENT);
169 		if (rc) {
170 			ocs_log_err(ocs, "ocs_dma_alloc els_rsp failed\n");
171 			ocs_io_pool_free(io_pool);
172 			return NULL;
173  		}
174 	}
175 
176 	return io_pool;
177 }
178 
179 /**
180  * @brief Free IO objects pool
181  *
182  * @par Description
183  * The pool of IO objects are freed.
184  *
185  * @param io_pool Pointer to IO pool object.
186  *
187  * @return Returns 0 on success, or a negative error code value on failure.
188  */
189 int32_t
ocs_io_pool_free(ocs_io_pool_t * io_pool)190 ocs_io_pool_free(ocs_io_pool_t *io_pool)
191 {
192 	ocs_t *ocs;
193 	uint32_t i;
194 	ocs_io_t *io;
195 
196 	if (io_pool != NULL) {
197 		ocs = io_pool->ocs;
198 		for (i = 0; i < io_pool->io_num_ios; i++) {
199 			io = ocs_pool_get_instance(io_pool->pool, i);
200 			if (!io)
201 				continue;
202 			ocs_scsi_tgt_io_exit(io);
203 			ocs_scsi_ini_io_exit(io);
204 			if (io->sgl) {
205 				ocs_free(ocs, io->sgl, sizeof(*io->sgl) * io->sgl_allocated);
206 			}
207 			ocs_dma_free(ocs, &io->cmdbuf);
208 			ocs_dma_free(ocs, &io->rspbuf);
209 			ocs_dma_free(ocs, &io->els_req);
210 			ocs_dma_free(ocs, &io->els_rsp);
211 		}
212 
213 		if (io_pool->pool != NULL) {
214 			ocs_pool_free(io_pool->pool);
215 		}
216 		ocs_lock_free(&io_pool->lock);
217 		ocs_free(ocs, io_pool, sizeof(*io_pool));
218 		ocs->xport->io_pool = NULL;
219 	}
220 
221 	return 0;
222 }
223 
ocs_io_pool_allocated(ocs_io_pool_t * io_pool)224 uint32_t ocs_io_pool_allocated(ocs_io_pool_t *io_pool)
225 {
226 	return io_pool->io_num_ios;
227 }
228 
229 /**
230  * @ingroup io_alloc
231  * @brief Allocate an object used to track an IO.
232  *
233  * @param io_pool Pointer to the IO pool.
234  *
235  * @return Returns the pointer to a new object, or NULL if none available.
236  */
237 ocs_io_t *
ocs_io_pool_io_alloc(ocs_io_pool_t * io_pool)238 ocs_io_pool_io_alloc(ocs_io_pool_t *io_pool)
239 {
240 	ocs_io_t *io = NULL;
241 	ocs_t *ocs;
242 
243 	ocs_assert(io_pool, NULL);
244 
245 	ocs = io_pool->ocs;
246 
247 	ocs_lock(&io_pool->lock);
248 	if ((io = ocs_pool_get(io_pool->pool)) != NULL) {
249 		ocs_unlock(&io_pool->lock);
250 
251 		io->io_type = OCS_IO_TYPE_MAX;
252 		io->hio_type = OCS_HW_IO_MAX;
253 		io->hio = NULL;
254 		io->transferred = 0;
255 		io->ocs = ocs;
256 		io->timeout = 0;
257 		io->sgl_count = 0;
258 		io->tgt_task_tag = 0;
259 		io->init_task_tag = 0;
260 		io->hw_tag = 0;
261 		io->display_name = "pending";
262 		io->seq_init = 0;
263 		io->els_req_free = 0;
264 		io->mgmt_functions = &io_mgmt_functions;
265 		io->io_free = 0;
266 		ocs_atomic_add_return(&ocs->xport->io_active_count, 1);
267 		ocs_atomic_add_return(&ocs->xport->io_total_alloc, 1);
268 	} else {
269 		ocs_unlock(&io_pool->lock);
270 	}
271 	return io;
272 }
273 
274 /**
275  * @ingroup io_alloc
276  * @brief Free an object used to track an IO.
277  *
278  * @param io_pool Pointer to IO pool object.
279  * @param io Pointer to the IO object.
280  */
281 void
ocs_io_pool_io_free(ocs_io_pool_t * io_pool,ocs_io_t * io)282 ocs_io_pool_io_free(ocs_io_pool_t *io_pool, ocs_io_t *io)
283 {
284 	ocs_t *ocs;
285 	ocs_hw_io_t *hio = NULL;
286 
287 	ocs_assert(io_pool);
288 
289 	ocs = io_pool->ocs;
290 
291 	ocs_lock(&io_pool->lock);
292 		hio = io->hio;
293 		io->hio = NULL;
294 		ocs_pool_put(io_pool->pool, io);
295 	ocs_unlock(&io_pool->lock);
296 
297 	if (hio) {
298 		ocs_hw_io_free(&ocs->hw, hio);
299 	}
300 	io->io_free = 1;
301 	ocs_atomic_sub_return(&ocs->xport->io_active_count, 1);
302 	ocs_atomic_add_return(&ocs->xport->io_total_free, 1);
303 }
304 
305 /**
306  * @ingroup io_alloc
307  * @brief Find an I/O given it's node and ox_id.
308  *
309  * @param ocs Driver instance's software context.
310  * @param node Pointer to node.
311  * @param ox_id OX_ID to find.
312  * @param rx_id RX_ID to find (0xffff for unassigned).
313  */
314 ocs_io_t *
ocs_io_find_tgt_io(ocs_t * ocs,ocs_node_t * node,uint16_t ox_id,uint16_t rx_id)315 ocs_io_find_tgt_io(ocs_t *ocs, ocs_node_t *node, uint16_t ox_id, uint16_t rx_id)
316 {
317 	ocs_io_t	*io = NULL;
318 
319 	ocs_lock(&node->active_ios_lock);
320 		ocs_list_foreach(&node->active_ios, io)
321 			if ((io->cmd_tgt && (io->init_task_tag == ox_id)) &&
322 			    ((rx_id == 0xffff) || (io->tgt_task_tag == rx_id))) {
323 				break;
324 			}
325 	ocs_unlock(&node->active_ios_lock);
326 	return io;
327 }
328 
329 /**
330  * @ingroup io_alloc
331  * @brief Return IO context given the instance index.
332  *
333  * @par Description
334  * Returns a pointer to the IO context given by the instance index.
335  *
336  * @param ocs Pointer to driver structure.
337  * @param index IO instance index to return.
338  *
339  * @return Returns a pointer to the IO context, or NULL if not found.
340  */
341 ocs_io_t *
ocs_io_get_instance(ocs_t * ocs,uint32_t index)342 ocs_io_get_instance(ocs_t *ocs, uint32_t index)
343 {
344 	ocs_xport_t *xport = ocs->xport;
345 	ocs_io_pool_t *io_pool = xport->io_pool;
346 	return ocs_pool_get_instance(io_pool->pool, index);
347 }
348 
349 /**
350  * @brief Generate IO context ddump data.
351  *
352  * The ddump data for an IO context is generated.
353  *
354  * @param textbuf Pointer to text buffer.
355  * @param io Pointer to IO context.
356  *
357  * @return None.
358  */
359 
360 void
ocs_ddump_io(ocs_textbuf_t * textbuf,ocs_io_t * io)361 ocs_ddump_io(ocs_textbuf_t *textbuf, ocs_io_t *io)
362 {
363 	ocs_ddump_section(textbuf, "io", io->instance_index);
364 	ocs_ddump_value(textbuf, "display_name", "%s", io->display_name);
365 	ocs_ddump_value(textbuf, "node_name", "%s", io->node->display_name);
366 
367 	ocs_ddump_value(textbuf, "ref_count", "%d", ocs_ref_read_count(&io->ref));
368 	ocs_ddump_value(textbuf, "io_type", "%d", io->io_type);
369 	ocs_ddump_value(textbuf, "hio_type", "%d", io->hio_type);
370 	ocs_ddump_value(textbuf, "cmd_tgt", "%d", io->cmd_tgt);
371 	ocs_ddump_value(textbuf, "cmd_ini", "%d", io->cmd_ini);
372 	ocs_ddump_value(textbuf, "send_abts", "%d", io->send_abts);
373 	ocs_ddump_value(textbuf, "init_task_tag", "0x%x", io->init_task_tag);
374 	ocs_ddump_value(textbuf, "tgt_task_tag", "0x%x", io->tgt_task_tag);
375 	ocs_ddump_value(textbuf, "hw_tag", "0x%x", io->hw_tag);
376 	ocs_ddump_value(textbuf, "tag", "0x%x", io->tag);
377 	ocs_ddump_value(textbuf, "timeout", "%d", io->timeout);
378 	ocs_ddump_value(textbuf, "tmf_cmd", "%d", io->tmf_cmd);
379 	ocs_ddump_value(textbuf, "abort_rx_id", "0x%x", io->abort_rx_id);
380 
381 	ocs_ddump_value(textbuf, "busy", "%d", ocs_io_busy(io));
382 	ocs_ddump_value(textbuf, "transferred", "%zu", io->transferred);
383 	ocs_ddump_value(textbuf, "auto_resp", "%d", io->auto_resp);
384 	ocs_ddump_value(textbuf, "exp_xfer_len", "%d", io->exp_xfer_len);
385 	ocs_ddump_value(textbuf, "xfer_req", "%d", io->xfer_req);
386 	ocs_ddump_value(textbuf, "seq_init", "%d", io->seq_init);
387 
388 	ocs_ddump_value(textbuf, "alloc_link", "%d", ocs_list_on_list(&io->io_alloc_link));
389 	ocs_ddump_value(textbuf, "pending_link", "%d", ocs_list_on_list(&io->io_pending_link));
390 	ocs_ddump_value(textbuf, "backend_link", "%d", ocs_list_on_list(&io->link));
391 
392 	if (io->hio) {
393 		ocs_ddump_value(textbuf, "hw_tag", "%#x", io->hio->reqtag);
394 		ocs_ddump_value(textbuf, "hw_xri", "%#x", io->hio->indicator);
395 		ocs_ddump_value(textbuf, "hw_type", "%#x", io->hio->type);
396 	} else {
397 		ocs_ddump_value(textbuf, "hw_tag", "%s", "pending");
398 		ocs_ddump_value(textbuf, "hw_xri", "%s", "pending");
399 		ocs_ddump_value(textbuf, "hw_type", "%s", "pending");
400 	}
401 
402 	ocs_scsi_ini_ddump(textbuf, OCS_SCSI_DDUMP_IO, io);
403 	ocs_scsi_tgt_ddump(textbuf, OCS_SCSI_DDUMP_IO, io);
404 
405 	ocs_ddump_endsection(textbuf, "io", io->instance_index);
406 }
407 
408 void
ocs_mgmt_io_list(ocs_textbuf_t * textbuf,void * object)409 ocs_mgmt_io_list(ocs_textbuf_t *textbuf, void *object)
410 {
411 
412 	/* Readonly values */
413 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "display_name");
414 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "init_task_tag");
415 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "tag");
416 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "transferred");
417 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "auto_resp");
418 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "exp_xfer_len");
419 	ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_RD, "xfer_req");
420 }
421 
422 int
ocs_mgmt_io_get(ocs_textbuf_t * textbuf,char * parent,char * name,void * object)423 ocs_mgmt_io_get(ocs_textbuf_t *textbuf, char *parent, char *name, void *object)
424 {
425 	char qualifier[80];
426 	int retval = -1;
427 	ocs_io_t *io = (ocs_io_t *) object;
428 
429 	snprintf(qualifier, sizeof(qualifier), "%s/io[%d]", parent, io->instance_index);
430 
431 	/* If it doesn't start with my qualifier I don't know what to do with it */
432 	if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
433 		char *unqualified_name = name + strlen(qualifier) +1;
434 
435 		/* See if it's a value I can supply */
436 		if (ocs_strcmp(unqualified_name, "display_name") == 0) {
437 			ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", io->display_name);
438 			retval = 0;
439 		} else if (ocs_strcmp(unqualified_name, "init_task_tag") == 0) {
440 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "init_task_tag", "0x%x", io->init_task_tag);
441 			retval = 0;
442 		} else if (ocs_strcmp(unqualified_name, "tgt_task_tag") == 0) {
443 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tgt_task_tag", "0x%x", io->tgt_task_tag);
444 			retval = 0;
445 		} else if (ocs_strcmp(unqualified_name, "hw_tag") == 0) {
446 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_tag", "0x%x", io->hw_tag);
447 			retval = 0;
448 		} else if (ocs_strcmp(unqualified_name, "tag") == 0) {
449 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tag", "0x%x", io->tag);
450 			retval = 0;
451 		} else if (ocs_strcmp(unqualified_name, "transferred") == 0) {
452 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "transferred", "%zu", io->transferred);
453 			retval = 0;
454 		} else if (ocs_strcmp(unqualified_name, "auto_resp") == 0) {
455 			ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "auto_resp", io->auto_resp);
456 			retval = 0;
457 		} else if (ocs_strcmp(unqualified_name, "exp_xfer_len") == 0) {
458 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "exp_xfer_len", "%d", io->exp_xfer_len);
459 			retval = 0;
460 		} else if (ocs_strcmp(unqualified_name, "xfer_req") == 0) {
461 			ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "xfer_req", "%d", io->xfer_req);
462 			retval = 0;
463 		}
464 	}
465 
466 	return retval;
467 }
468 
469 void
ocs_mgmt_io_get_all(ocs_textbuf_t * textbuf,void * object)470 ocs_mgmt_io_get_all(ocs_textbuf_t *textbuf, void *object)
471 {
472 	ocs_io_t *io = (ocs_io_t *) object;
473 
474 	ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "display_name", io->display_name);
475 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "init_task_tag", "0x%x", io->init_task_tag);
476 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tgt_task_tag", "0x%x", io->tgt_task_tag);
477 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_tag", "0x%x", io->hw_tag);
478 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "tag", "0x%x", io->tag);
479 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "transferred", "%zu", io->transferred);
480 	ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RD, "auto_resp", io->auto_resp);
481 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "exp_xfer_len", "%d", io->exp_xfer_len);
482 	ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "xfer_req", "%d", io->xfer_req);
483 
484 }
485