Print this page
NEX-3856 panic is occurred in module "fct" due to a NULL pointer dereference
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Reviewed by: Steve Peng <steve.peng@nexenta.com>
NEX-3277 Panic of both nodes in failover time (FC clients)
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-2787 Multiple comstar / fibre channel / qlt threads stuck waiting on locks with a spinning interrupt thread
Reviewed by: Rob Gittins <rob.gittins@nexenta.com>
Approved by: Jean McCormack <jean.mccormack@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/io/comstar/port/fct/fct_impl.h
+++ new/usr/src/uts/common/io/comstar/port/fct/fct_impl.h
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 + * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
23 24 * Use is subject to license terms.
24 25 */
25 26 #ifndef _FCT_IMPL_H
26 27 #define _FCT_IMPL_H
27 28
28 29 #ifdef __cplusplus
29 30 extern "C" {
30 31 #endif
31 32
32 33 #define RSCN_OPTION_VERIFY 0x0001
33 34
34 35 typedef enum fct_li_state {
35 36 LI_STATE_DO_FLOGI = 0, /* FLOGI handled by FCA */
36 37 LI_STATE_FINI_TOPOLOGY, /* Finalize topology */
37 38 LI_STATE_N2N_PLOGI, /* In case of a N2N connection */
38 39
39 40 LI_STATE_DO_FCLOGIN, /* Login into 0xFFFFFD */
40 41 LI_STATE_DO_SCR, /* State change registration */
41 42
42 43 LI_STATE_DO_NSLOGIN, /* Login into 0xFFFFFC */
43 44 LI_STATE_DO_RNN, /* Register node name */
44 45 LI_STATE_DO_RCS, /* Register classes of service */
45 46 LI_STATE_DO_RFT, /* Register FC-4 types */
46 47 LI_STATE_DO_RSPN, /* Register symbolic port name */
47 48 LI_STATE_DO_RSNN, /* Register symbolic node name */
48 49
49 50 LI_STATE_MAX /* Not a real state */
50 51 } fct_li_state_t;
51 52
52 53 #define LI_STATE_START 0
53 54 #define LI_STATE_MASK 0x3F
54 55 /* Next state depends on the return value */
55 56 #define LI_STATE_FLAG_CMD_RETCHECK 0x40
56 57 /* Link init cmd is still outstanding */
57 58 #define LI_STATE_FLAG_CMD_WAITING 0x80
58 59 /* Flag to indicate that link info is not available yet */
59 60 #define LI_STATE_FLAG_NO_LI_YET 0x100
60 61
61 62 #define FCT_MAX_CACHED_CMDS 256
62 63 #define USEC_ELS_TIMEOUT (10 * 1000 * 1000)
63 64 #define USEC_SOL_TIMEOUT (10 * 1000 * 1000)
64 65 #define USEC_DEREG_RP_TIMEOUT (25 * 1000 * 1000)
|
↓ open down ↓ |
32 lines elided |
↑ open up ↑ |
65 66 #define USEC_DEREG_RP_INTERVAL (2 * 1000 * 1000)
66 67
67 68 struct fct_i_cmd;
68 69 typedef void (* fct_icmd_cb_t)(struct fct_i_cmd *icmd);
69 70 typedef struct fct_i_cmd {
70 71 fct_cmd_t *icmd_cmd;
71 72 uint32_t icmd_alloc_size;
72 73 fct_struct_id_t icmd_struct_id;
73 74 uint32_t icmd_flags;
74 75 clock_t icmd_start_time;
75 - struct fct_i_cmd *icmd_next; /* iport_abort_queue and irp */
76 + list_node_t icmd_node; /* iport_abort_queue and irp */
76 77 struct fct_i_cmd *icmd_solcmd_next; /* iport_solcmd_queue */
77 78 fct_icmd_cb_t icmd_cb;
78 79 void *icmd_cb_private;
79 80 } fct_i_cmd_t;
80 81
81 82 /*
82 83 * icmd_flags
83 84 */
84 85 #define ICMD_SESSION_AFFECTING 0x0002
85 86 #define ICMD_IN_IRP_QUEUE 0x0004
86 87 #define ICMD_BEING_ABORTED 0x0008
87 88 #define ICMD_KNOWN_TO_FCA 0x0020
88 89 #define ICMD_FCA_ABORT_CALLED 0x0040
89 90 #define ICMD_CMD_COMPLETE 0x0080
90 91 #define ICMD_IN_TRANSITION 0x0100
91 92 #define ICMD_ABTS_RECEIVED 0x0200
92 93 #define ICMD_IMPLICIT 0x0400
93 94 #define ICMD_IMPLICIT_CMD_HAS_RESOURCE 0x0800
94 95 /* High order are debug flags */
95 96 #define ICMD_ELS_PROCESSING_STARTED 0x80000000
96 97
97 98 /*
98 99 * For solicited commands, there's only 3 states:
99 100 * 1) it's new. We need send it to FCA. ICMD_SOLCMD_NEW is set
100 101 * 2) it's running. We are waiting for completion.
101 102 * 3) it's completed. We need free it. ICMD_CMD_COMPLETE is set
102 103 * ICMD_SOLCMD_NEW and ICMD_CMD_COMPLETE should not be set in the same time
103 104 */
104 105 #define ICMD_IN_SOLCMD_QUEUE 0x010000
105 106 #define ICMD_SOLCMD_NEW 0x020000
106 107
107 108 typedef struct fct_i_remote_port {
108 109 fct_remote_port_t *irp_rp;
|
↓ open down ↓ |
23 lines elided |
↑ open up ↑ |
109 110 uint32_t irp_alloc_size;
110 111 fct_struct_id_t irp_struct_id;
111 112 krwlock_t irp_lock;
112 113
113 114 /* For queueing to local port */
114 115 struct fct_i_remote_port *irp_next;
115 116
116 117 /* For queueing to handle elses */
117 118 struct fct_i_remote_port *irp_discovery_next;
118 119
119 - fct_i_cmd_t *irp_els_list;
120 + list_t irp_els_list;
120 121
121 122 /*
122 123 * sa stands for session affecting, nsa is non session affecting.
123 124 * The els counts only represent elses under progress not the ones
124 125 * that are terminated. active_xchg_count covers everything including
125 126 * the ones waiting to be terminated.
126 127 */
127 128 uint16_t irp_sa_elses_count;
128 129 uint16_t irp_nsa_elses_count;
129 130 uint16_t irp_fcp_xchg_count;
130 131 uint16_t irp_nonfcp_xchg_count;
131 132
132 133 uint32_t irp_flags;
133 134 clock_t irp_deregister_timer;
134 135 uint32_t irp_dereg_count;
135 136
136 137 uint32_t irp_portid;
|
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
137 138 uint8_t irp_id[24];
138 139 uint32_t irp_rcvd_prli_params;
139 140 uint32_t irp_sent_prli_params;
140 141
141 142 /*
142 143 * Most HBAs will only register symbolic node name instead of port name,
143 144 * so we use SNN as session alias.
144 145 */
145 146 stmf_scsi_session_t *irp_session;
146 147 char *irp_snn;
148 + uint16_t irp_snn_len;
147 149
148 150 /* items will be filled in ns cmd */
149 151 uint8_t irp_fc4types[32]; /* FC-4 types */
150 152 char *irp_spn; /* port symbolic name */
153 + uint16_t irp_spn_len;
151 154 uint32_t irp_cos; /* class of service */
152 155
153 156 uint32_t irp_rscn_counter;
154 157 } fct_i_remote_port_t;
155 158
156 159 /*
157 160 * structure used for fct_rls_cb() callback private data
158 161 */
159 162 typedef struct fct_rls_cb_data {
160 163 struct fct_port_link_status *fct_link_status;
161 164 fct_status_t fct_els_res;
162 165 } fct_rls_cb_data_t;
163 166
164 167 /*
165 168 * irp flags
166 169 */
167 170 #define IRP_PLOGI_DONE 0x0001
168 171 #define IRP_PRLI_DONE 0x0002
169 172 #define IRP_IN_DISCOVERY_QUEUE 0x0004
170 173 #define IRP_FCP_CLEANUP 0x0008
171 174 #define IRP_SESSION_CLEANUP (IRP_FCP_CLEANUP | 0x0010)
172 175 #define IRP_HANDLE_OPENED 0x0020
173 176 #define IRP_SCSI_SESSION_STARTED 0x0040
174 177 #define IRP_RSCN_QUEUED 0x0080
175 178 #define IRP_SOL_PLOGI_IN_PROGRESS 0x0100
176 179
177 180 typedef struct fct_cmd_slot {
178 181 fct_i_cmd_t *slot_cmd;
179 182 uint16_t slot_no;
180 183 uint16_t slot_next;
181 184 uint8_t slot_uniq_cntr;
182 185 } fct_cmd_slot_t;
183 186 #define FCT_SLOT_EOL 0xffff
184 187
185 188 #define FCT_HASH_TABLE_SIZE 256
186 189 #define FCT_LOOP_HASH(portid) (portid & 0xff)
187 190 #define FCT_FABRIC_HASH(portid) (((portid & 0x1f00) | \
188 191 ((portid & 0x70000)>>3)) >> 8)
189 192 #define FCT_PORTID_HASH_FUNC(portid) \
190 193 ((portid & 0xFFFF00)?FCT_FABRIC_HASH(portid):FCT_LOOP_HASH(portid))
191 194
192 195 typedef struct fct_i_local_port {
193 196 fct_local_port_t *iport_port;
194 197 uint32_t iport_alloc_size;
195 198 fct_struct_id_t iport_struct_id;
196 199
197 200 struct fct_i_local_port *iport_next;
198 201 struct fct_i_local_port *iport_prev;
199 202
200 203 char *iport_alias;
201 204 char iport_alias_mem[16];
202 205 uint8_t iport_id[24]; /* scsi_devid_desc_t */
203 206 krwlock_t iport_lock;
204 207 uint32_t iport_flags;
205 208 uint16_t iport_link_state;
206 209 uint8_t iport_state:7,
207 210 iport_state_not_acked:1;
208 211 uint8_t iport_offline_prstate;
209 212 struct fct_link_info iport_link_info;
210 213
211 214 fct_i_remote_port_t **iport_rp_slots;
212 215 fct_i_remote_port_t **iport_rp_tb;
213 216 uint32_t iport_nrps_login; /* currently logged in */
214 217 uint32_t iport_nrps; /* items in hash table */
215 218 uint64_t iport_last_change;
216 219
217 220 /*
218 221 * These variables are used to manage fct_cmd_t cache for SCSI traffic
219 222 */
220 223 /*
221 224 * Total # of cmds allocated by the driver. Some of which are free
222 225 * and sitting on iport_cached_cmdlist. And some are executing.
223 226 */
224 227 uint32_t iport_total_alloced_ncmds;
|
↓ open down ↓ |
64 lines elided |
↑ open up ↑ |
225 228
226 229 /*
227 230 * Max active cmds in last interval (10 or 30 seconds)
228 231 */
229 232 uint32_t iport_max_active_ncmds;
230 233
231 234 /*
232 235 * # of free cmds sitting on the iport_cached_cmdlist
233 236 */
234 237 uint32_t iport_cached_ncmds;
235 - struct fct_i_cmd *iport_cached_cmdlist;
238 + list_t iport_cached_cmdlist;
236 239 kmutex_t iport_cached_cmd_lock;
237 240
238 241 /*
239 242 * To release free cmds periodically
240 243 */
241 244 clock_t iport_cmdcheck_clock;
242 245
243 246 uint16_t iport_task_green_limit;
244 247 uint16_t iport_task_yellow_limit;
245 248 uint16_t iport_task_red_limit;
246 249 /* cmd slots */
247 250 uint16_t iport_nslots_free;
248 251
249 252 /* upper 16 bits is just a counter to avoid ABA issues */
250 253 uint32_t iport_next_free_slot;
251 254
252 255 uint8_t iport_login_retry; /* for flogi and N2N plogi */
253 256 uint8_t iport_link_old_topology;
|
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
254 257 uint8_t iport_link_cleanup_retry;
255 258 clock_t iport_li_cmd_timeout; /* for li state m/c */
256 259 fct_cmd_slot_t *iport_cmd_slots;
257 260
258 261 /* worker thread data */
259 262 ddi_taskq_t *iport_worker_taskq;
260 263 kmutex_t iport_worker_lock;
261 264 kcondvar_t iport_worker_cv;
262 265 struct fct_i_event *iport_event_head;
263 266 struct fct_i_event *iport_event_tail;
264 - struct fct_i_cmd *iport_abort_queue;
265 - struct fct_i_cmd **iport_ppicmd_term;
267 + list_t iport_abort_queue;
268 + struct fct_i_cmd *iport_ppicmd_term;
266 269
267 270 /* link initialization */
268 271 fct_status_t iport_li_comp_status;
269 272 enum fct_li_state iport_li_state;
270 273
271 274 /* solicited cmd link */
272 275 struct fct_i_cmd *iport_solcmd_queue;
273 276
274 277 /* rpwe = remote port with pending els(es) */
275 278 fct_i_remote_port_t *iport_rpwe_head;
276 279 fct_i_remote_port_t *iport_rpwe_tail;
277 280 kstat_t *iport_kstat_portstat;
278 281 ksema_t iport_rls_sema;
279 282 fct_rls_cb_data_t iport_rls_cb_data;
280 283 } fct_i_local_port_t;
281 284
282 285 #define IPORT_FLOGI_DONE(iport) PORT_FLOGI_DONE(&(iport)->iport_link_info)
283 286
284 287 /*
285 288 * iport flags
286 289 */
287 290 #define IPORT_WORKER_RUNNING 0x0001
288 291 #define IPORT_TERMINATE_WORKER 0x0002
289 292 #define IPORT_WORKER_DOING_TIMEDWAIT 0x0004
290 293 #define IPORT_WORKER_DOING_WAIT 0x0008
291 294 #define IPORT_FLAG_PORT_OFFLINED 0x0010
292 295 #define IPORT_ALLOW_UNSOL_FLOGI 0x0020
293 296
294 297 #define IS_WORKER_SLEEPING(iport) ((iport)->iport_flags & \
295 298 (IPORT_WORKER_DOING_TIMEDWAIT | IPORT_WORKER_DOING_WAIT))
296 299
297 300 /* Limits for scsi task load of local port */
298 301 #define FCT_TASK_GREEN_LIMIT 80
299 302 #define FCT_TASK_YELLOW_LIMIT 90
300 303 #define FCT_TASK_RED_LIMIT 95
301 304
302 305 typedef struct fct_i_event {
303 306 struct fct_i_event *event_next;
304 307 int event_type;
305 308 } fct_i_event_t;
306 309
307 310 typedef enum { /* Seggested action values for discovery thread */
308 311 DISC_ACTION_NO_WORK = 0,
309 312 DISC_ACTION_RESCAN = 1,
310 313 DISC_ACTION_DELAY_RESCAN = 2,
311 314 DISC_ACTION_USE_SHORT_DELAY = 4
312 315 } disc_action_t;
313 316
314 317 /*
315 318 * Local port state definitions
316 319 * NOTE that every time there is a state change, the newly set bit suggests
317 320 * the action. So far there are 3 actions S_PORT_CLEANUP, S_ADAPTER_FATAL
318 321 * and S_INIT_LINK.
319 322 */
320 323 #define S_RCVD_LINK_DOWN 0x01
321 324 #define S_RCVD_LINK_UP 0x02
322 325 #define S_LINK_ONLINE 0x04
323 326 #define S_INIT_LINK 0x08
324 327 #define S_PORT_CLEANUP 0x10
325 328
326 329 #define PORT_STATE_LINK_DOWN 0x00
327 330 #define PORT_STATE_LINK_INIT_START (S_RCVD_LINK_UP | S_LINK_ONLINE |\
328 331 S_INIT_LINK)
329 332 #define PORT_STATE_LINK_INIT_DONE (S_LINK_ONLINE)
330 333 #define PORT_STATE_LINK_UP_CLEANING (S_RCVD_LINK_UP | S_PORT_CLEANUP)
331 334 #define PORT_STATE_LINK_DOWN_CLEANING (S_RCVD_LINK_DOWN | S_PORT_CLEANUP)
332 335
333 336 /*
334 337 * Internal events
335 338 */
336 339 #define FCT_I_EVENT_LINK_INIT_DONE 0x80
337 340 #define FCT_I_EVENT_CLEANUP_POLL 0x81
338 341
339 342 /*
340 343 * Offline processing states, used by worker thread.
341 344 */
342 345 #define FCT_OPR_DONE 0
343 346 #define FCT_OPR_START 1
344 347 #define FCT_OPR_CMD_CLEANUP_WAIT 2
345 348 #define FCT_OPR_INT_CLEANUP_WAIT 3
346 349
347 350 /*
348 351 * Check time
349 352 */
350 353 #define FCT_CMDLIST_CHECK_SECONDS 10
351 354
352 355 /*
353 356 * Define frequently used macros
354 357 */
355 358 #define ICMD_TO_CT(x_icmd) \
356 359 ((fct_sol_ct_t *)(x_icmd)->icmd_cmd->cmd_specific)
357 360
358 361 #define ICMD_TO_ELS(x_icmd) \
359 362 ((fct_els_t *)(x_icmd)->icmd_cmd->cmd_specific)
360 363
361 364 #define ICMD_TO_IPORT(x_icmd) \
362 365 ((fct_i_local_port_t *)(x_icmd)->icmd_cmd->cmd_port->port_fct_private)
363 366
364 367 #define ICMD_TO_PORT(x_icmd) \
365 368 ((x_icmd)->icmd_cmd->cmd_port)
366 369
367 370 #define ICMD_TO_IRP(x_icmd) \
368 371 ((fct_i_remote_port_t *)(x_icmd)->icmd_cmd->cmd_rp->rp_fct_private)
369 372
370 373 #define CMD_TO_ICMD(x_cmd) ((fct_i_cmd_t *)(x_cmd)->cmd_fct_private)
371 374
372 375 #define RP_TO_IRP(x_rp) ((fct_i_remote_port_t *)(x_rp)->rp_fct_private)
373 376
374 377 #define PORT_TO_IPORT(x_port) \
375 378 ((fct_i_local_port_t *)(x_port)->port_fct_private)
376 379
377 380 #define FCT_IS_ELS_ACC(x_icmd) \
378 381 (((x_icmd)->icmd_cmd->cmd_comp_status == FCT_SUCCESS) && \
379 382 (ICMD_TO_ELS(x_icmd)->els_resp_payload[0] == ELS_OP_ACC))
380 383
381 384 #define FCT_IS_CT_ACC(x_icmd) \
382 385 (((x_icmd)->icmd_cmd->cmd_comp_status == FCT_SUCCESS) && \
383 386 (ICMD_TO_CT(x_icmd)->ct_resp_payload[8] == 0x80) &&\
384 387 (ICMD_TO_CT(x_icmd)->ct_resp_payload[9] == 0x02))
385 388
386 389 #define IPORT_IN_NS_TOPO(x_iport) \
387 390 ((x_iport)->iport_link_info.port_topology & PORT_TOPOLOGY_FABRIC_BIT)
388 391
389 392 #define IS_LOGO_ELS(icmd) \
390 393 (ICMD_TO_ELS(icmd)->els_req_payload[0] == ELS_OP_LOGO)
391 394
392 395 stmf_status_t fct_xfer_scsi_data(scsi_task_t *task,
393 396 stmf_data_buf_t *dbuf, uint32_t ioflags);
394 397 stmf_status_t fct_send_scsi_status(scsi_task_t *task, uint32_t ioflags);
395 398 fct_i_remote_port_t *fct_portid_to_portptr(fct_i_local_port_t *iport,
396 399 uint32_t portid);
397 400 fct_i_remote_port_t *fct_lookup_irp_by_nodewwn(fct_i_local_port_t *iport,
398 401 uint8_t *nodewwn);
399 402 fct_i_remote_port_t *fct_lookup_irp_by_portwwn(fct_i_local_port_t *iport,
400 403 uint8_t *portwwn);
401 404 void fct_queue_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp);
402 405 void fct_deque_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp);
403 406 int fct_implicitly_logo_all(fct_i_local_port_t *iport, int force_implicit);
404 407 void fct_post_implicit_logo(fct_cmd_t *cmd);
405 408 void fct_rehash(fct_i_local_port_t *iport);
406 409 uint8_t fct_local_port_cleanup_done(fct_i_local_port_t *iport);
407 410 void fct_handle_rcvd_abts(fct_cmd_t *cmd);
408 411 void fct_fill_abts_acc(fct_cmd_t *cmd);
409 412 void fct_q_for_termination_lock_held(fct_i_local_port_t *iport,
410 413 fct_i_cmd_t *icmd, fct_status_t s);
411 414 disc_action_t fct_handle_port_offline(fct_i_local_port_t *iport);
412 415 disc_action_t fct_cmd_terminator(fct_i_local_port_t *iport);
413 416 void fct_cmd_free(fct_cmd_t *cmd);
414 417 void fct_scsi_task_free(scsi_task_t *task);
415 418 stmf_status_t fct_scsi_abort(stmf_local_port_t *lport, int abort_cmd,
416 419 void *arg, uint32_t flags);
417 420 stmf_status_t fct_info(uint32_t cmd, stmf_local_port_t *lport,
418 421 void *arg, uint8_t *buf, uint32_t *bufsizep);
419 422 void fct_event_handler(stmf_local_port_t *lport, int eventid,
420 423 void *arg, uint32_t flags);
421 424 uint16_t fct_alloc_cmd_slot(fct_i_local_port_t *iport, fct_cmd_t *cmd);
422 425 void fct_post_to_discovery_queue(fct_i_local_port_t *iport,
423 426 fct_i_remote_port_t *irp, fct_i_cmd_t *icmd);
424 427 fct_cmd_t *fct_create_solct(fct_local_port_t *port, fct_remote_port_t *rp,
425 428 uint16_t ctop, fct_icmd_cb_t icmdcb);
426 429 fct_cmd_t *fct_create_solels(fct_local_port_t *port, fct_remote_port_t *rp,
427 430 int implicit, uchar_t elsop, uint32_t wkdid, fct_icmd_cb_t icmdcb);
|
↓ open down ↓ |
152 lines elided |
↑ open up ↑ |
428 431 void fct_handle_solct(fct_cmd_t *cmd);
429 432 void fct_post_to_solcmd_queue(fct_local_port_t *port, fct_cmd_t *cmd);
430 433 void fct_logo_cb(fct_i_cmd_t *icmd);
431 434 void fct_link_init_cb(fct_i_cmd_t *icmd);
432 435 void fct_gsnn_cb(fct_i_cmd_t *icmd);
433 436 void fct_gcs_cb(fct_i_cmd_t *icmd);
434 437 void fct_gft_cb(fct_i_cmd_t *icmd);
435 438 void fct_gspn_cb(fct_i_cmd_t *icmd);
436 439 void fct_rls_cb(fct_i_cmd_t *icmd);
437 440 disc_action_t fct_process_link_init(fct_i_local_port_t *iport);
441 +void fct_cmd_unlink_els(fct_i_remote_port_t *irp, fct_i_cmd_t *icmd);
438 442
439 443 #ifdef __cplusplus
440 444 }
441 445 #endif
442 446
443 447 #endif /* _FCT_IMPL_H */
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX