xref: /freebsd/sys/dev/smartpqi/smartpqi_helper.c (revision 2f06449d64298fe508e3c585b45effd69a72d696)
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 /*
30  * Function used to validate the adapter health.
31  */
32 boolean_t
pqisrc_ctrl_offline(pqisrc_softstate_t * softs)33 pqisrc_ctrl_offline(pqisrc_softstate_t *softs)
34 {
35 	DBG_FUNC("IN\n");
36 
37 	DBG_FUNC("OUT\n");
38 
39 	return !softs->ctrl_online;
40 }
41 /* Function used set/clear legacy INTx bit in Legacy Interrupt INTx
42  * mask clear pqi register
43  */
44 void
pqisrc_configure_legacy_intx(pqisrc_softstate_t * softs,boolean_t enable_intx)45 pqisrc_configure_legacy_intx(pqisrc_softstate_t *softs, boolean_t enable_intx)
46 {
47 	uint32_t intx_mask;
48 
49  	DBG_FUNC("IN\n");
50 
51 	intx_mask = PCI_MEM_GET32(softs, 0, PQI_LEGACY_INTR_MASK_CLR);
52 	intx_mask |= PQISRC_LEGACY_INTX_MASK;
53 	PCI_MEM_PUT32(softs, 0, PQI_LEGACY_INTR_MASK_CLR ,intx_mask);
54 
55  	DBG_FUNC("OUT\n");
56 }
57 
58 /*
59  * Function used to take exposed devices to OS as offline.
60  */
61 void
pqisrc_take_devices_offline(pqisrc_softstate_t * softs)62 pqisrc_take_devices_offline(pqisrc_softstate_t *softs)
63 {
64 	pqi_scsi_dev_t *device = NULL;
65 	int i;
66 
67 	DBG_FUNC("IN\n");
68 	for(i = 0; i < PQI_MAX_DEVICES; i++) {
69 		device = softs->dev_list[i];
70 		if(device == NULL)
71 			continue;
72 		pqisrc_remove_device(softs, device);
73 	}
74 
75 	DBG_FUNC("OUT\n");
76 }
77 
78 /*
79  * Function used to take adapter offline.
80  */
81 void
pqisrc_take_ctrl_offline(pqisrc_softstate_t * softs)82 pqisrc_take_ctrl_offline(pqisrc_softstate_t *softs)
83 {
84 	DBG_FUNC("IN\n");
85 
86 	int lockupcode = 0;
87 
88 	softs->ctrl_online = false;
89 
90 	if (SIS_IS_KERNEL_PANIC(softs)) {
91 		lockupcode = PCI_MEM_GET32(softs, &softs->ioa_reg->mb[7], LEGACY_SIS_SRCV_OFFSET_MAILBOX_7);
92         DBG_ERR("Controller FW is not running, Lockup code = %x\n", lockupcode);
93 	}
94 	else {
95 	pqisrc_trigger_nmi_sis(softs);
96 	}
97 
98 	os_complete_outstanding_cmds_nodevice(softs);
99 	pqisrc_wait_for_rescan_complete(softs);
100 	pqisrc_take_devices_offline(softs);
101 
102 	DBG_FUNC("OUT\n");
103 }
104 
105 /*
106  * Timer handler for the adapter heart-beat.
107  */
108 void
pqisrc_heartbeat_timer_handler(pqisrc_softstate_t * softs)109 pqisrc_heartbeat_timer_handler(pqisrc_softstate_t *softs)
110 {
111 	uint8_t take_offline = false;
112 	uint64_t new_heartbeat;
113 	static uint32_t running_ping_cnt = 0;
114 
115 	DBG_FUNC("IN\n");
116 
117 	new_heartbeat = CTRLR_HEARTBEAT_CNT(softs);
118 	DBG_IO("heartbeat old=%lx new=%lx\n", softs->prev_heartbeat_count, new_heartbeat);
119 
120 	if (new_heartbeat == softs->prev_heartbeat_count) {
121 		take_offline = true;
122 		goto take_ctrl_offline;
123 	}
124 
125 #if 1
126 	/* print every 30 calls (should print once/minute) */
127 	running_ping_cnt++;
128 
129 	if ((running_ping_cnt % 30) == 0)
130 		print_all_counters(softs, COUNTER_FLAG_ONLY_NON_ZERO);
131 #endif
132 
133 	softs->prev_heartbeat_count = new_heartbeat;
134 
135 take_ctrl_offline:
136 	if (take_offline){
137 		DBG_ERR("controller is offline\n");
138 		os_stop_heartbeat_timer(softs);
139 		pqisrc_take_ctrl_offline(softs);
140 	}
141 	DBG_FUNC("OUT\n");
142 }
143 
144 /*
145  * Conditional variable management routine for internal commands.
146  */
147 int
pqisrc_wait_on_condition(pqisrc_softstate_t * softs,rcb_t * rcb,uint32_t timeout_in_msec)148 pqisrc_wait_on_condition(pqisrc_softstate_t *softs, rcb_t *rcb,
149 				uint32_t timeout_in_msec)
150 {
151 	DBG_FUNC("IN\n");
152 
153 	int ret = PQI_STATUS_SUCCESS;
154 
155 	/* 1 msec = 500 usec * 2 */
156 	uint32_t loop_cnt = timeout_in_msec * 2;
157 	uint32_t i = 0;
158 
159 	while (rcb->req_pending == true) {
160 		OS_SLEEP(500); /* Micro sec */
161 		/* Polling needed for FreeBSD : since ithread routine is not scheduled
162 		 * during bootup, we could use polling until interrupts are
163 		 * enabled (using 'if (cold)'to check for the boot time before
164 		 * interrupts are enabled). */
165 		IS_POLLING_REQUIRED(softs);
166 
167 		if ((timeout_in_msec != TIMEOUT_INFINITE) && (i++ == loop_cnt)) {
168 			DBG_ERR("ERR: Requested cmd timed out !!!\n");
169 			ret = PQI_STATUS_TIMEOUT;
170 			rcb->timedout = true;
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. */
189 boolean_t
pqisrc_device_equal(pqi_scsi_dev_t * dev1,pqi_scsi_dev_t * dev2)190 pqisrc_device_equal(pqi_scsi_dev_t *dev1,
191 	pqi_scsi_dev_t *dev2)
192 {
193 	return dev1->wwid == dev2->wwid;
194 }
195 
196 /* Function used to validate the device scsi3addr. */
197 boolean_t
pqisrc_scsi3addr_equal(uint8_t * scsi3addr1,uint8_t * scsi3addr2)198 pqisrc_scsi3addr_equal(uint8_t *scsi3addr1, uint8_t *scsi3addr2)
199 {
200 	return memcmp(scsi3addr1, scsi3addr2, 8) == 0;
201 }
202 
203 /* Function used to validate hba_lunid */
204 boolean_t
pqisrc_is_hba_lunid(uint8_t * scsi3addr)205 pqisrc_is_hba_lunid(uint8_t *scsi3addr)
206 {
207 	return pqisrc_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID);
208 }
209 
210 /* Function used to validate type of device */
211 boolean_t
pqisrc_is_logical_device(pqi_scsi_dev_t * device)212 pqisrc_is_logical_device(pqi_scsi_dev_t *device)
213 {
214 	return !device->is_physical_device;
215 }
216 
217 /* Function used to sanitize inquiry string */
218 void
pqisrc_sanitize_inquiry_string(unsigned char * s,int len)219 pqisrc_sanitize_inquiry_string(unsigned char *s, int len)
220 {
221 	boolean_t terminated = false;
222 
223 	DBG_FUNC("IN\n");
224 
225 	for (; len > 0; (--len, ++s)) {
226 		if (*s == 0)
227 			terminated = true;
228 		if (terminated || *s < 0x20 || *s > 0x7e)
229 			*s = ' ';
230 	}
231 
232 	DBG_FUNC("OUT\n");
233 }
234 
235 static char *raid_levels[] = {
236 	"RAID 0",
237 	"RAID 4",
238 	"RAID 1(1+0)",
239 	"RAID 5",
240 	"RAID 5+1",
241 	"RAID 6",
242 	"RAID 1(Triple)",
243 };
244 
245 /* Get the RAID level from the index */
246 char *
pqisrc_raidlevel_to_string(uint8_t raid_level)247 pqisrc_raidlevel_to_string(uint8_t raid_level)
248 {
249 	DBG_FUNC("IN\n");
250 	if (raid_level < ARRAY_SIZE(raid_levels))
251 		return raid_levels[raid_level];
252 	DBG_FUNC("OUT\n");
253 
254 	return " ";
255 }
256 
257 /* Debug routine for displaying device info */
pqisrc_display_device_info(pqisrc_softstate_t * softs,char * action,pqi_scsi_dev_t * device)258 void pqisrc_display_device_info(pqisrc_softstate_t *softs,
259 	char *action, pqi_scsi_dev_t *device)
260 {
261 	if (device->is_physical_device) {
262 		DBG_NOTE("%s scsi BTL %d:%d:%d:  %.8s %.16s %-12s "
263 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
264 		action,
265 		device->bus,
266 		device->target,
267 		device->lun,
268 		device->vendor,
269 		device->model,
270 		"Physical",
271 		device->offload_config ? '+' : '-',
272 		device->offload_enabled_pending ? '+' : '-',
273 		device->expose_device ? '+' : '-',
274 		device->queue_depth);
275 	} else if (device->devtype == RAID_DEVICE) {
276 		DBG_NOTE("%s scsi BTL %d:%d:%d:  %.8s %.16s %-12s "
277 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
278 		action,
279 		device->bus,
280 		device->target,
281 		device->lun,
282 		device->vendor,
283 		device->model,
284 		"Controller",
285 		device->offload_config ? '+' : '-',
286 		device->offload_enabled_pending ? '+' : '-',
287 		device->expose_device ? '+' : '-',
288 		device->queue_depth);
289 	} else if (device->devtype == CONTROLLER_DEVICE) {
290 		DBG_NOTE("%s scsi BTL %d:%d:%d:  %.8s %.16s %-12s "
291 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
292 		action,
293 		device->bus,
294 		device->target,
295 		device->lun,
296 		device->vendor,
297 		device->model,
298 		"External",
299 		device->offload_config ? '+' : '-',
300 		device->offload_enabled_pending ? '+' : '-',
301 		device->expose_device ? '+' : '-',
302 		device->queue_depth);
303 	} else {
304 		DBG_NOTE("%s scsi BTL %d:%d:%d:  %.8s %.16s %-12s "
305 		"SSDSmartPathCap%c En%c Exp%c qd=%d devtype=%d\n",
306 		action,
307 		device->bus,
308 		device->target,
309 		device->lun,
310 		device->vendor,
311 		device->model,
312 		pqisrc_raidlevel_to_string(device->raid_level),
313 		device->offload_config ? '+' : '-',
314 		device->offload_enabled_pending ? '+' : '-',
315 		device->expose_device ? '+' : '-',
316 		device->queue_depth,
317 		device->devtype);
318 	pqisrc_raidlevel_to_string(device->raid_level); /* To use this function */
319 	}
320 }
321 
322 /* validate the structure sizes */
323 void
check_struct_sizes(void)324 check_struct_sizes(void)
325 {
326 
327     ASSERT(sizeof(SCSI3Addr_struct)== 2);
328     ASSERT(sizeof(PhysDevAddr_struct) == 8);
329     ASSERT(sizeof(LogDevAddr_struct)== 8);
330     ASSERT(sizeof(LUNAddr_struct)==8);
331     ASSERT(sizeof(RequestBlock_struct) == 20);
332     ASSERT(sizeof(MoreErrInfo_struct)== 8);
333     ASSERT(sizeof(ErrorInfo_struct)== 48);
334     /* Checking the size of IOCTL_Command_struct for both
335        64 bit and 32 bit system*/
336     ASSERT(sizeof(IOCTL_Command_struct)== 86 ||
337            sizeof(IOCTL_Command_struct)== 82);
338     ASSERT(sizeof(struct bmic_host_wellness_driver_version)== 42);
339     ASSERT(sizeof(struct bmic_host_wellness_time)== 20);
340     ASSERT(sizeof(struct pqi_dev_adminq_cap)== 8);
341     ASSERT(sizeof(struct admin_q_param)== 4);
342     ASSERT(sizeof(struct pqi_registers)== 256);
343     ASSERT(sizeof(struct ioa_registers)== 4128);
344     ASSERT(sizeof(struct pqi_pref_settings)==4);
345     ASSERT(sizeof(struct pqi_cap)== 20);
346     ASSERT(sizeof(iu_header_t)== 4);
347     ASSERT(sizeof(gen_adm_req_iu_t)== 64);
348     ASSERT(sizeof(gen_adm_resp_iu_t)== 64);
349     ASSERT(sizeof(op_q_params) == 9);
350     ASSERT(sizeof(raid_path_error_info_elem_t)== 276);
351     ASSERT(sizeof(aio_path_error_info_elem_t)== 276);
352     ASSERT(sizeof(struct init_base_struct)== 24);
353     ASSERT(sizeof(pqi_iu_layer_desc_t)== 16);
354     ASSERT(sizeof(pqi_dev_cap_t)== 576);
355     ASSERT(sizeof(pqi_aio_req_t)== 128);
356     ASSERT(sizeof(pqisrc_raid_req_t)== 128);
357     ASSERT(sizeof(pqi_raid_tmf_req_t)== 32);
358     ASSERT(sizeof(pqi_aio_tmf_req_t)== 32);
359     ASSERT(sizeof(struct pqi_io_response)== 16);
360     ASSERT(sizeof(struct sense_header_scsi)== 8);
361     ASSERT(sizeof(reportlun_header_t)==8);
362     ASSERT(sizeof(reportlun_ext_entry_t)== 24);
363     ASSERT(sizeof(reportlun_data_ext_t)== 32);
364     ASSERT(sizeof(raidmap_data_t)==8);
365     ASSERT(sizeof(pqisrc_raid_map_t)== 8256);
366     ASSERT(sizeof(bmic_ident_ctrl_t)== 325);
367     ASSERT(sizeof(bmic_ident_physdev_t)==2048);
368 
369 }
370 
371 #if 0
372 uint32_t
373 pqisrc_count_num_scsi_active_requests_on_dev(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
374 {
375 	uint32_t i, active_io = 0;
376 	rcb_t* rcb;
377 
378 	for(i = 1; i <= softs->max_outstanding_io; i++) {
379 		rcb = &softs->rcb[i];
380 		if(rcb && IS_OS_SCSICMD(rcb) && (rcb->dvp == device) && rcb->req_pending) {
381 			active_io++;
382 		}
383 	}
384 	return active_io;
385 }
386 
387 void
388 check_device_pending_commands_to_complete(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
389 {
390 	uint32_t tag = softs->max_outstanding_io, active_requests;
391 	uint64_t timeout = 0, delay_in_usec = 1000; /* In micro Seconds  */
392 	rcb_t* rcb;
393 
394 	DBG_FUNC("IN\n");
395 
396 	active_requests = pqisrc_count_num_scsi_active_requests_on_dev(softs, device);
397 
398 	DBG_WARN("Device Outstanding IO count = %u\n", active_requests);
399 
400 	if(!active_requests)
401 		return;
402 
403 	do {
404 		rcb = &softs->rcb[tag];
405 		if(rcb && IS_OS_SCSICMD(rcb) && (rcb->dvp == device) && rcb->req_pending) {
406 			OS_SLEEP(delay_in_usec);
407 			timeout += delay_in_usec;
408 		}
409 		else
410 			tag--;
411 		if(timeout >= PQISRC_PENDING_IO_TIMEOUT_USEC) {
412 			DBG_WARN("timed out waiting for pending IO\n");
413 			return;
414 		}
415 	} while(tag);
416 }
417 #endif
418 
419 void
pqisrc_wait_for_device_commands_to_complete(pqisrc_softstate_t * softs,pqi_scsi_dev_t * device)420 pqisrc_wait_for_device_commands_to_complete(pqisrc_softstate_t *softs, pqi_scsi_dev_t *device)
421 {
422 	uint64_t timeout_in_usec = 0, delay_in_usec = 1000; /* In microseconds */
423 
424 	DBG_FUNC("IN\n");
425 
426 	if(!softs->ctrl_online)
427 		return;
428 
429 #if PQISRC_DEVICE_IO_COUNTER
430 	DBG_WARN_BTL(device,"Device Outstanding IO count = %lu\n", pqisrc_read_device_active_io(softs, device));
431 
432 	while(pqisrc_read_device_active_io(softs, device)) {
433 		OS_BUSYWAIT(delay_in_usec); /* In microseconds */
434 		if(!softs->ctrl_online) {
435 			DBG_WARN("Controller Offline was detected.\n");
436 		}
437 		timeout_in_usec += delay_in_usec;
438 		if(timeout_in_usec >= PQISRC_PENDING_IO_TIMEOUT_USEC) {
439 			DBG_WARN_BTL(device,"timed out waiting for pending IO. DeviceOutStandingIo's=%lu\n",
440                                  pqisrc_read_device_active_io(softs, device));
441 			return;
442 		}
443 	}
444 #else
445 	check_device_pending_commands_to_complete(softs, device);
446 #endif
447 }
448