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 /*
32 * Function used to validate the adapter health.
33 */
pqisrc_ctrl_offline(pqisrc_softstate_t * softs)34 boolean_t pqisrc_ctrl_offline(pqisrc_softstate_t *softs)
35 {
36 DBG_FUNC("IN\n");
37
38 DBG_FUNC("OUT\n");
39
40 return !softs->ctrl_online;
41 }
42
43 /* Function used set/clear legacy INTx bit in Legacy Interrupt INTx
44 * mask clear pqi register
45 */
pqisrc_configure_legacy_intx(pqisrc_softstate_t * softs,boolean_t enable_intx)46 void pqisrc_configure_legacy_intx(pqisrc_softstate_t *softs, boolean_t enable_intx)
47 {
48 uint32_t intx_mask;
49 uint32_t *reg_addr = NULL;
50
51 DBG_FUNC("IN\n");
52
53 if (enable_intx)
54 reg_addr = &softs->pqi_reg->legacy_intr_mask_clr;
55 else
56 reg_addr = &softs->pqi_reg->legacy_intr_mask_set;
57
58 intx_mask = PCI_MEM_GET32(softs, reg_addr, PQI_LEGACY_INTR_MASK_CLR);
59 intx_mask |= PQISRC_LEGACY_INTX_MASK;
60 PCI_MEM_PUT32(softs, reg_addr, PQI_LEGACY_INTR_MASK_CLR ,intx_mask);
61
62 DBG_FUNC("OUT\n");
63 }
64
65 /*
66 * Function used to take exposed devices to OS as offline.
67 */
pqisrc_take_devices_offline(pqisrc_softstate_t * softs)68 void pqisrc_take_devices_offline(pqisrc_softstate_t *softs)
69 {
70 pqi_scsi_dev_t *device = NULL;
71 int i,j;
72
73 DBG_FUNC("IN\n");
74 for(i = 0; i < PQI_MAX_DEVICES; i++) {
75 for(j = 0; j < PQI_MAX_MULTILUN; j++) {
76 if(softs->device_list[i][j] == NULL)
77 continue;
78 device = softs->device_list[i][j];
79 pqisrc_remove_device(softs, device);
80 }
81 }
82
83 DBG_FUNC("OUT\n");
84 }
85
86 /*
87 * Function used to take adapter offline.
88 */
pqisrc_take_ctrl_offline(pqisrc_softstate_t * softs)89 void pqisrc_take_ctrl_offline(pqisrc_softstate_t *softs)
90 {
91
92 DBG_FUNC("IN\n");
93
94 softs->ctrl_online = false;
95 pqisrc_trigger_nmi_sis(softs);
96 os_complete_outstanding_cmds_nodevice(softs);
97 pqisrc_take_devices_offline(softs);
98
99 DBG_FUNC("OUT\n");
100 }
101
102 /*
103 * Timer handler for the adapter heart-beat.
104 */
pqisrc_heartbeat_timer_handler(pqisrc_softstate_t * softs)105 void pqisrc_heartbeat_timer_handler(pqisrc_softstate_t *softs)
106 {
107 uint64_t num_intrs;
108 uint8_t take_offline = false;
109
110 DBG_FUNC("IN\n");
111
112 num_intrs = OS_ATOMIC64_READ(softs, num_intrs);
113
114 if (PQI_NEW_HEARTBEAT_MECHANISM(softs)) {
115 if (CTRLR_HEARTBEAT_CNT(softs) == softs->prev_heartbeat_count) {
116 take_offline = true;
117 goto take_ctrl_offline;
118 }
119 softs->prev_heartbeat_count = CTRLR_HEARTBEAT_CNT(softs);
120 DBG_INFO("CTRLR_HEARTBEAT_CNT(softs) = %lx \
121 softs->prev_heartbeat_count = %lx\n",
122 CTRLR_HEARTBEAT_CNT(softs), softs->prev_heartbeat_count);
123 } else {
124 if (num_intrs == softs->prev_num_intrs) {
125 softs->num_heartbeats_requested++;
126 if (softs->num_heartbeats_requested > PQI_MAX_HEARTBEAT_REQUESTS) {
127 take_offline = true;
128 goto take_ctrl_offline;
129 }
130 softs->pending_events[PQI_EVENT_HEARTBEAT].pending = true;
131
132 pqisrc_ack_all_events((void*)softs);
133
134 } else {
135 softs->num_heartbeats_requested = 0;
136 }
137 softs->prev_num_intrs = num_intrs;
138 }
139
140 take_ctrl_offline:
141 if (take_offline){
142 DBG_ERR("controller is offline\n");
143 pqisrc_take_ctrl_offline(softs);
144 os_stop_heartbeat_timer(softs);
145 }
146 DBG_FUNC("OUT\n");
147 }
148
149 /*
150 * Conditional variable management routine for internal commands.
151 */
pqisrc_wait_on_condition(pqisrc_softstate_t * softs,rcb_t * rcb)152 int pqisrc_wait_on_condition(pqisrc_softstate_t *softs, rcb_t *rcb){
153
154 DBG_FUNC("IN\n");
155
156 int ret = PQI_STATUS_SUCCESS;
157 uint32_t loop_cnt = 0;
158
159 while (rcb->req_pending == true) {
160 OS_SLEEP(500); /* Micro sec */
161
162 /*Polling needed for FreeBSD : since ithread routine is not scheduled
163 during bootup, we could use polling until interrupts are
164 enabled (using 'if (cold)'to check for the boot time before
165 interrupts are enabled). */
166 IS_POLLING_REQUIRED(softs);
167
168 if (loop_cnt++ == PQISRC_CMD_TIMEOUT_CNT) {
169 DBG_ERR("ERR: Requested cmd timed out !!!\n");
170 ret = PQI_STATUS_TIMEOUT;
171 break;
172 }
173
174 if (pqisrc_ctrl_offline(softs)) {
175 DBG_ERR("Controller is Offline");
176 ret = PQI_STATUS_FAILURE;
177 break;
178 }
179
180 }
181 rcb->req_pending = true;
182
183 DBG_FUNC("OUT\n");
184
185 return ret;
186 }
187
188 /* Function used to validate the device wwid. */
pqisrc_device_equal(pqi_scsi_dev_t * dev1,pqi_scsi_dev_t * dev2)189 boolean_t pqisrc_device_equal(pqi_scsi_dev_t *dev1,
190 pqi_scsi_dev_t *dev2)
191 {
192 return dev1->wwid == dev2->wwid;
193 }
194
195 /* Function used to validate the device scsi3addr. */
pqisrc_scsi3addr_equal(uint8_t * scsi3addr1,uint8_t * scsi3addr2)196 boolean_t pqisrc_scsi3addr_equal(uint8_t *scsi3addr1, uint8_t *scsi3addr2)
197 {
198 return memcmp(scsi3addr1, scsi3addr2, 8) == 0;
199 }
200
201 /* Function used to validate hba_lunid */
pqisrc_is_hba_lunid(uint8_t * scsi3addr)202 boolean_t pqisrc_is_hba_lunid(uint8_t *scsi3addr)
203 {
204 return pqisrc_scsi3addr_equal(scsi3addr, (uint8_t*)RAID_CTLR_LUNID);
205 }
206
207 /* Function used to validate type of device */
pqisrc_is_logical_device(pqi_scsi_dev_t * device)208 boolean_t pqisrc_is_logical_device(pqi_scsi_dev_t *device)
209 {
210 return !device->is_physical_device;
211 }
212
213 /* Function used to sanitize inquiry string */
pqisrc_sanitize_inquiry_string(unsigned char * s,int len)214 void pqisrc_sanitize_inquiry_string(unsigned char *s, int len)
215 {
216 boolean_t terminated = false;
217
218 DBG_FUNC("IN\n");
219
220 for (; len > 0; (--len, ++s)) {
221 if (*s == 0)
222 terminated = true;
223 if (terminated || *s < 0x20 || *s > 0x7e)
224 *s = ' ';
225 }
226
227 DBG_FUNC("OUT\n");
228 }
229
230 static char *raid_levels[] = {
231 "RAID 0",
232 "RAID 4",
233 "RAID 1(1+0)",
234 "RAID 5",
235 "RAID 5+1",
236 "RAID ADG",
237 "RAID 1(ADM)",
238 };
239
240 /* Get the RAID level from the index */
pqisrc_raidlevel_to_string(uint8_t raid_level)241 char *pqisrc_raidlevel_to_string(uint8_t raid_level)
242 {
243 DBG_FUNC("IN\n");
244 if (raid_level < ARRAY_SIZE(raid_levels))
245 return raid_levels[raid_level];
246 DBG_FUNC("OUT\n");
247
248 return " ";
249 }
250
251 /* Debug routine for displaying device info */
pqisrc_display_device_info(pqisrc_softstate_t * softs,char * action,pqi_scsi_dev_t * device)252 void pqisrc_display_device_info(pqisrc_softstate_t *softs,
253 char *action, pqi_scsi_dev_t *device)
254 {
255 DBG_INFO( "%s scsi BTL %d:%d:%d: %.8s %.16s %-12s SSDSmartPathCap%c En%c Exp%c qd=%d\n",
256 action,
257 device->bus,
258 device->target,
259 device->lun,
260 device->vendor,
261 device->model,
262 pqisrc_raidlevel_to_string(device->raid_level),
263 device->offload_config ? '+' : '-',
264 device->offload_enabled_pending ? '+' : '-',
265 device->expose_device ? '+' : '-',
266 device->queue_depth);
267 pqisrc_raidlevel_to_string(device->raid_level); /* To use this function */
268 }
269
270 /* validate the structure sizes */
check_struct_sizes()271 void check_struct_sizes()
272 {
273
274 ASSERT(sizeof(SCSI3Addr_struct)== 2);
275 ASSERT(sizeof(PhysDevAddr_struct) == 8);
276 ASSERT(sizeof(LogDevAddr_struct)== 8);
277 ASSERT(sizeof(LUNAddr_struct)==8);
278 ASSERT(sizeof(RequestBlock_struct) == 20);
279 ASSERT(sizeof(MoreErrInfo_struct)== 8);
280 ASSERT(sizeof(ErrorInfo_struct)== 48);
281 ASSERT(sizeof(IOCTL_Command_struct)== 86);
282 ASSERT(sizeof(struct bmic_host_wellness_driver_version)== 42);
283 ASSERT(sizeof(struct bmic_host_wellness_time)== 20);
284 ASSERT(sizeof(struct pqi_dev_adminq_cap)== 8);
285 ASSERT(sizeof(struct admin_q_param)== 4);
286 ASSERT(sizeof(struct pqi_registers)== 256);
287 ASSERT(sizeof(struct ioa_registers)== 4128);
288 ASSERT(sizeof(struct pqi_pref_settings)==4);
289 ASSERT(sizeof(struct pqi_cap)== 20);
290 ASSERT(sizeof(iu_header_t)== 4);
291 ASSERT(sizeof(gen_adm_req_iu_t)== 64);
292 ASSERT(sizeof(gen_adm_resp_iu_t)== 64);
293 ASSERT(sizeof(op_q_params) == 9);
294 ASSERT(sizeof(raid_path_error_info_elem_t)== 276);
295 ASSERT(sizeof(aio_path_error_info_elem_t)== 276);
296 ASSERT(sizeof(struct init_base_struct)== 24);
297 ASSERT(sizeof(pqi_iu_layer_desc_t)== 16);
298 ASSERT(sizeof(pqi_dev_cap_t)== 576);
299 ASSERT(sizeof(pqi_aio_req_t)== 128);
300 ASSERT(sizeof(pqisrc_raid_req_t)== 128);
301 ASSERT(sizeof(pqi_tmf_req_t)== 32);
302 ASSERT(sizeof(struct pqi_io_response)== 16);
303 ASSERT(sizeof(struct sense_header_scsi)== 8);
304 ASSERT(sizeof(reportlun_header_t)==8);
305 ASSERT(sizeof(reportlun_ext_entry_t)== 24);
306 ASSERT(sizeof(reportlun_data_ext_t)== 32);
307 ASSERT(sizeof(raidmap_data_t)==8);
308 ASSERT(sizeof(pqisrc_raid_map_t)== 8256);
309 ASSERT(sizeof(bmic_ident_ctrl_t)== 325);
310 ASSERT(sizeof(bmic_ident_physdev_t)==2048);
311
312 }
313