xref: /src/sys/dev/smartpqi/smartpqi_helper.c (revision 7f54c65abc67f50363bbd2a68a980d23e69c9ef0)
1 /*-
2  * Copyright 2016-2025 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 const * softs)33 pqisrc_ctrl_offline(pqisrc_softstate_t const *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 	softs->ctrl_online = false;
87 
88 	if (SIS_IS_KERNEL_PANIC(softs)) {
89 		int lockupcode = PCI_MEM_GET32(softs, &softs->ioa_reg->mb[7], LEGACY_SIS_SRCV_OFFSET_MAILBOX_7);
90         DBG_ERR("Controller FW is not running, Lockup code = %x\n", lockupcode);
91 	}
92 	else {
93 	pqisrc_trigger_nmi_sis(softs);
94 	}
95 
96 	os_complete_outstanding_cmds_nodevice(softs);
97 	pqisrc_wait_for_rescan_complete(softs);
98 	pqisrc_take_devices_offline(softs);
99 
100 	DBG_FUNC("OUT\n");
101 }
102 
103 /*
104  * Timer handler for the adapter heart-beat.
105  */
106 void
pqisrc_heartbeat_timer_handler(pqisrc_softstate_t * softs)107 pqisrc_heartbeat_timer_handler(pqisrc_softstate_t *softs)
108 {
109 	uint8_t take_offline = false;
110 	uint64_t new_heartbeat;
111 	static uint32_t running_ping_cnt = 0;
112 
113 	DBG_FUNC("IN\n");
114 
115 	new_heartbeat = CTRLR_HEARTBEAT_CNT(softs);
116 	DBG_IO("heartbeat old=%lx new=%lx\n", softs->prev_heartbeat_count, new_heartbeat);
117 
118 	if (new_heartbeat == softs->prev_heartbeat_count) {
119 		take_offline = true;
120 		goto take_ctrl_offline;
121 	}
122 
123 #if 1
124 	/* print every 30 calls (should print once/minute) */
125 	running_ping_cnt++;
126 
127 	if ((running_ping_cnt % 30) == 0)
128 		print_all_counters(softs, COUNTER_FLAG_ONLY_NON_ZERO);
129 #endif
130 
131 	softs->prev_heartbeat_count = new_heartbeat;
132 
133 take_ctrl_offline:
134 	if (take_offline){
135 		DBG_ERR("controller is offline\n");
136 		os_stop_heartbeat_timer(softs);
137 		pqisrc_take_ctrl_offline(softs);
138 	}
139 	DBG_FUNC("OUT\n");
140 }
141 
142 /*
143  * Conditional variable management routine for internal commands.
144  */
145 int
pqisrc_wait_on_condition(pqisrc_softstate_t * softs,rcb_t * rcb,uint32_t timeout_in_msec)146 pqisrc_wait_on_condition(pqisrc_softstate_t *softs, rcb_t *rcb,
147 				uint32_t timeout_in_msec)
148 {
149 	DBG_FUNC("IN\n");
150 
151 	int ret = PQI_STATUS_SUCCESS;
152 
153 	/* 1 msec = 500 usec * 2 */
154 	uint32_t loop_cnt = timeout_in_msec * 2;
155 	uint32_t i = 0;
156 
157 	while (rcb->req_pending == true) {
158 		OS_SLEEP(500); /* Micro sec */
159 		/* Polling needed for FreeBSD : since ithread routine is not scheduled
160 		 * during bootup, we could use polling until interrupts are
161 		 * enabled (using 'if (cold)'to check for the boot time before
162 		 * interrupts are enabled). */
163 		IS_POLLING_REQUIRED(softs);
164 
165 		if ((timeout_in_msec != TIMEOUT_INFINITE) && (i++ == loop_cnt)) {
166 			DBG_ERR("ERR: Requested cmd timed out !!!\n");
167 			ret = PQI_STATUS_TIMEOUT;
168 			rcb->timedout = true;
169 			break;
170 		}
171 
172 		if (pqisrc_ctrl_offline(softs)) {
173 			DBG_ERR("Controller is Offline\n");
174 			ret = PQI_STATUS_FAILURE;
175 			break;
176 		}
177 
178 	}
179 	rcb->req_pending = true;
180 
181 	DBG_FUNC("OUT\n");
182 
183 	return ret;
184 }
185 
186 /* Function used to validate the device wwid. */
187 boolean_t
pqisrc_device_equal(pqi_scsi_dev_t const * dev1,pqi_scsi_dev_t const * dev2)188 pqisrc_device_equal(pqi_scsi_dev_t const *dev1,
189 	pqi_scsi_dev_t const *dev2)
190 {
191 	return dev1->wwid == dev2->wwid;
192 }
193 
194 /* Function used to validate the device scsi3addr. */
195 boolean_t
pqisrc_scsi3addr_equal(uint8_t const * scsi3addr1,uint8_t const * scsi3addr2)196 pqisrc_scsi3addr_equal(uint8_t const *scsi3addr1, uint8_t const *scsi3addr2)
197 {
198 	return memcmp(scsi3addr1, scsi3addr2, 8) == 0;
199 }
200 
201 /* Function used to validate hba_lunid */
202 boolean_t
pqisrc_is_hba_lunid(uint8_t const * scsi3addr)203 pqisrc_is_hba_lunid(uint8_t const *scsi3addr)
204 {
205 	return pqisrc_scsi3addr_equal(scsi3addr, RAID_CTLR_LUNID);
206 }
207 
208 /* Function used to validate type of device */
209 boolean_t
pqisrc_is_logical_device(pqi_scsi_dev_t const * device)210 pqisrc_is_logical_device(pqi_scsi_dev_t const *device)
211 {
212 	return !device->is_physical_device;
213 }
214 
215 /* Function used to sanitize inquiry string */
216 void
pqisrc_sanitize_inquiry_string(unsigned char * s,int len)217 pqisrc_sanitize_inquiry_string(unsigned char *s, int len)
218 {
219 	boolean_t terminated = false;
220 
221 	DBG_FUNC("IN\n");
222 
223 	for (; len > 0; (--len, ++s)) {
224 		if (*s == 0)
225 			terminated = true;
226 		if (terminated || *s < 0x20 || *s > 0x7e)
227 			*s = ' ';
228 	}
229 
230 	DBG_FUNC("OUT\n");
231 }
232 
233 static char *raid_levels[] = {
234 	"RAID 0",
235 	"RAID 4",
236 	"RAID 1(1+0)",
237 	"RAID 5",
238 	"RAID 5+1",
239 	"RAID 6",
240 	"RAID 1(Triple)",
241 };
242 
243 /* Get the RAID level from the index */
244 char *
pqisrc_raidlevel_to_string(uint8_t raid_level)245 pqisrc_raidlevel_to_string(uint8_t raid_level)
246 {
247 	DBG_FUNC("IN\n");
248 	if (raid_level < ARRAY_SIZE(raid_levels))
249 		return raid_levels[raid_level];
250 	DBG_FUNC("OUT\n");
251 
252 	return " ";
253 }
254 
255 /* Debug routine for displaying device info */
pqisrc_display_device_info(pqisrc_softstate_t * softs,char const * action,pqi_scsi_dev_t * device)256 void pqisrc_display_device_info(pqisrc_softstate_t *softs,
257 	char const *action, pqi_scsi_dev_t *device)
258 {
259 	if (device->is_physical_device) {
260 		DBG_NOTE("%s scsi B%d:T%d:L%d  %.8s %.16s %-12s "
261 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
262 		action,
263 		device->bus,
264 		device->target,
265 		device->lun,
266 		device->vendor,
267 		device->model,
268 		"Physical",
269 		device->offload_config ? '+' : '-',
270 		device->offload_enabled_pending ? '+' : '-',
271 		device->expose_device ? '+' : '-',
272 		device->queue_depth);
273 	} else if (device->devtype == RAID_DEVICE) {
274 		DBG_NOTE("%s scsi B%d:T%d:L%d  %.8s %.16s %-12s "
275 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
276 		action,
277 		device->bus,
278 		device->target,
279 		device->lun,
280 		device->vendor,
281 		device->model,
282 		"Controller",
283 		device->offload_config ? '+' : '-',
284 		device->offload_enabled_pending ? '+' : '-',
285 		device->expose_device ? '+' : '-',
286 		device->queue_depth);
287 	} else if (device->devtype == CONTROLLER_DEVICE) {
288 		DBG_NOTE("%s scsi B%d:T%d:L%d  %.8s %.16s %-12s "
289 		"SSDSmartPathCap%c En%c Exp%c qd=%d\n",
290 		action,
291 		device->bus,
292 		device->target,
293 		device->lun,
294 		device->vendor,
295 		device->model,
296 		"External",
297 		device->offload_config ? '+' : '-',
298 		device->offload_enabled_pending ? '+' : '-',
299 		device->expose_device ? '+' : '-',
300 		device->queue_depth);
301 	} else {
302 		DBG_NOTE("%s scsi B%d:T%d:L%d  %.8s %.16s %-12s "
303 		"SSDSmartPathCap%c En%c Exp%c qd=%d devtype=%d\n",
304 		action,
305 		device->bus,
306 		device->target,
307 		device->lun,
308 		device->vendor,
309 		device->model,
310 		pqisrc_raidlevel_to_string(device->raid_level),
311 		device->offload_config ? '+' : '-',
312 		device->offload_enabled_pending ? '+' : '-',
313 		device->expose_device ? '+' : '-',
314 		device->queue_depth,
315 		device->devtype);
316 	pqisrc_raidlevel_to_string(device->raid_level); /* To use this function */
317 	}
318 }
319 
320 /* validate the structure sizes */
321 void
check_struct_sizes(void)322 check_struct_sizes(void)
323 {
324 
325     ASSERT(sizeof(SCSI3Addr_struct)== 2);
326     ASSERT(sizeof(PhysDevAddr_struct) == 8);
327     ASSERT(sizeof(LogDevAddr_struct)== 8);
328     ASSERT(sizeof(LUNAddr_struct)==8);
329     ASSERT(sizeof(RequestBlock_struct) == 20);
330     ASSERT(sizeof(MoreErrInfo_struct)== 8);
331     ASSERT(sizeof(ErrorInfo_struct)== 48);
332     /* Checking the size of IOCTL_Command_struct for both
333        64 bit and 32 bit system*/
334     ASSERT(sizeof(IOCTL_Command_struct)== 86 ||
335            sizeof(IOCTL_Command_struct)== 82);
336     ASSERT(sizeof(BIG_IOCTL_Command_struct)== 88 ||
337            sizeof(BIG_IOCTL_Command_struct)== 84);
338     ASSERT(sizeof(struct bmic_host_wellness_driver_version)== 44);
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 microseconds  */
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