1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2021 Broadcom. All Rights Reserved. The term |
4 | * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. |
5 | */ |
6 | |
7 | #include "efct_driver.h" |
8 | #include "efct_hw.h" |
9 | #include "efct_io.h" |
10 | |
11 | struct efct_io_pool { |
12 | struct efct *efct; |
13 | spinlock_t lock; /* IO pool lock */ |
14 | u32 io_num_ios; /* Total IOs allocated */ |
15 | struct efct_io *ios[EFCT_NUM_SCSI_IOS]; |
16 | struct list_head freelist; |
17 | |
18 | }; |
19 | |
20 | struct efct_io_pool * |
21 | efct_io_pool_create(struct efct *efct, u32 num_sgl) |
22 | { |
23 | u32 i = 0; |
24 | struct efct_io_pool *io_pool; |
25 | struct efct_io *io; |
26 | |
27 | /* Allocate the IO pool */ |
28 | io_pool = kzalloc(size: sizeof(*io_pool), GFP_KERNEL); |
29 | if (!io_pool) |
30 | return NULL; |
31 | |
32 | io_pool->efct = efct; |
33 | INIT_LIST_HEAD(list: &io_pool->freelist); |
34 | /* initialize IO pool lock */ |
35 | spin_lock_init(&io_pool->lock); |
36 | |
37 | for (i = 0; i < EFCT_NUM_SCSI_IOS; i++) { |
38 | io = kzalloc(size: sizeof(*io), GFP_KERNEL); |
39 | if (!io) |
40 | break; |
41 | |
42 | io_pool->io_num_ios++; |
43 | io_pool->ios[i] = io; |
44 | io->tag = i; |
45 | io->instance_index = i; |
46 | |
47 | /* Allocate a response buffer */ |
48 | io->rspbuf.size = SCSI_RSP_BUF_LENGTH; |
49 | io->rspbuf.virt = dma_alloc_coherent(dev: &efct->pci->dev, |
50 | size: io->rspbuf.size, |
51 | dma_handle: &io->rspbuf.phys, GFP_KERNEL); |
52 | if (!io->rspbuf.virt) { |
53 | efc_log_err(efct, "dma_alloc rspbuf failed\n" ); |
54 | efct_io_pool_free(io_pool); |
55 | return NULL; |
56 | } |
57 | |
58 | /* Allocate SGL */ |
59 | io->sgl = kzalloc(size: sizeof(*io->sgl) * num_sgl, GFP_KERNEL); |
60 | if (!io->sgl) { |
61 | efct_io_pool_free(io_pool); |
62 | return NULL; |
63 | } |
64 | |
65 | io->sgl_allocated = num_sgl; |
66 | io->sgl_count = 0; |
67 | |
68 | INIT_LIST_HEAD(list: &io->list_entry); |
69 | list_add_tail(new: &io->list_entry, head: &io_pool->freelist); |
70 | } |
71 | |
72 | return io_pool; |
73 | } |
74 | |
75 | int |
76 | efct_io_pool_free(struct efct_io_pool *io_pool) |
77 | { |
78 | struct efct *efct; |
79 | u32 i; |
80 | struct efct_io *io; |
81 | |
82 | if (io_pool) { |
83 | efct = io_pool->efct; |
84 | |
85 | for (i = 0; i < io_pool->io_num_ios; i++) { |
86 | io = io_pool->ios[i]; |
87 | if (!io) |
88 | continue; |
89 | |
90 | kfree(objp: io->sgl); |
91 | dma_free_coherent(dev: &efct->pci->dev, |
92 | size: io->rspbuf.size, cpu_addr: io->rspbuf.virt, |
93 | dma_handle: io->rspbuf.phys); |
94 | memset(&io->rspbuf, 0, sizeof(struct efc_dma)); |
95 | } |
96 | |
97 | kfree(objp: io_pool); |
98 | efct->xport->io_pool = NULL; |
99 | } |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | struct efct_io * |
105 | efct_io_pool_io_alloc(struct efct_io_pool *io_pool) |
106 | { |
107 | struct efct_io *io = NULL; |
108 | struct efct *efct; |
109 | unsigned long flags = 0; |
110 | |
111 | efct = io_pool->efct; |
112 | |
113 | spin_lock_irqsave(&io_pool->lock, flags); |
114 | |
115 | if (!list_empty(head: &io_pool->freelist)) { |
116 | io = list_first_entry(&io_pool->freelist, struct efct_io, |
117 | list_entry); |
118 | list_del_init(entry: &io->list_entry); |
119 | } |
120 | |
121 | spin_unlock_irqrestore(lock: &io_pool->lock, flags); |
122 | |
123 | if (!io) |
124 | return NULL; |
125 | |
126 | io->io_type = EFCT_IO_TYPE_MAX; |
127 | io->hio_type = EFCT_HW_IO_MAX; |
128 | io->hio = NULL; |
129 | io->transferred = 0; |
130 | io->efct = efct; |
131 | io->timeout = 0; |
132 | io->sgl_count = 0; |
133 | io->tgt_task_tag = 0; |
134 | io->init_task_tag = 0; |
135 | io->hw_tag = 0; |
136 | io->display_name = "pending" ; |
137 | io->seq_init = 0; |
138 | io->io_free = 0; |
139 | io->release = NULL; |
140 | atomic_add_return(i: 1, v: &efct->xport->io_active_count); |
141 | atomic_add_return(i: 1, v: &efct->xport->io_total_alloc); |
142 | return io; |
143 | } |
144 | |
145 | /* Free an object used to track an IO */ |
146 | void |
147 | efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io) |
148 | { |
149 | struct efct *efct; |
150 | struct efct_hw_io *hio = NULL; |
151 | unsigned long flags = 0; |
152 | |
153 | efct = io_pool->efct; |
154 | |
155 | spin_lock_irqsave(&io_pool->lock, flags); |
156 | hio = io->hio; |
157 | io->hio = NULL; |
158 | io->io_free = 1; |
159 | INIT_LIST_HEAD(list: &io->list_entry); |
160 | list_add(new: &io->list_entry, head: &io_pool->freelist); |
161 | spin_unlock_irqrestore(lock: &io_pool->lock, flags); |
162 | |
163 | if (hio) |
164 | efct_hw_io_free(hw: &efct->hw, io: hio); |
165 | |
166 | atomic_sub_return(i: 1, v: &efct->xport->io_active_count); |
167 | atomic_add_return(i: 1, v: &efct->xport->io_total_free); |
168 | } |
169 | |
170 | /* Find an I/O given it's node and ox_id */ |
171 | struct efct_io * |
172 | efct_io_find_tgt_io(struct efct *efct, struct efct_node *node, |
173 | u16 ox_id, u16 rx_id) |
174 | { |
175 | struct efct_io *io = NULL; |
176 | unsigned long flags = 0; |
177 | u8 found = false; |
178 | |
179 | spin_lock_irqsave(&node->active_ios_lock, flags); |
180 | list_for_each_entry(io, &node->active_ios, list_entry) { |
181 | if ((io->cmd_tgt && io->init_task_tag == ox_id) && |
182 | (rx_id == 0xffff || io->tgt_task_tag == rx_id)) { |
183 | if (kref_get_unless_zero(kref: &io->ref)) |
184 | found = true; |
185 | break; |
186 | } |
187 | } |
188 | spin_unlock_irqrestore(lock: &node->active_ios_lock, flags); |
189 | return found ? io : NULL; |
190 | } |
191 | |