| 1 | /* |
|---|
| 2 | * (C) 2001 Clemson University and The University of Chicago |
|---|
| 3 | * |
|---|
| 4 | * See COPYING in top-level directory. |
|---|
| 5 | */ |
|---|
| 6 | |
|---|
| 7 | #ifndef __STATE_MACHINE_FNS_H |
|---|
| 8 | #define __STATE_MACHINE_FNS_H |
|---|
| 9 | |
|---|
| 10 | #include <stdio.h> |
|---|
| 11 | #include <string.h> |
|---|
| 12 | #include <assert.h> |
|---|
| 13 | #include <stdlib.h> /* sson */ |
|---|
| 14 | |
|---|
| 15 | #include "gossip.h" |
|---|
| 16 | #include "pvfs2-debug.h" |
|---|
| 17 | #include "state-machine.h" |
|---|
| 18 | #include "client-state-machine.h" |
|---|
| 19 | |
|---|
| 20 | struct PINT_frame_s |
|---|
| 21 | { |
|---|
| 22 | int task_id; |
|---|
| 23 | void *frame; |
|---|
| 24 | int error; |
|---|
| 25 | struct qlist_head link; |
|---|
| 26 | }; |
|---|
| 27 | |
|---|
| 28 | static struct PINT_state_s *PINT_pop_state(struct PINT_smcb *); |
|---|
| 29 | static void PINT_push_state(struct PINT_smcb *, struct PINT_state_s *); |
|---|
| 30 | static struct PINT_state_s *PINT_sm_task_map(struct PINT_smcb *smcb, int task_id); |
|---|
| 31 | static void PINT_sm_start_child_frames(struct PINT_smcb *smcb, int* children_started); |
|---|
| 32 | |
|---|
| 33 | /* Function: PINT_state_machine_halt(void) |
|---|
| 34 | Params: None |
|---|
| 35 | Returns: True |
|---|
| 36 | Synopsis: This function is used to shutdown the state machine |
|---|
| 37 | */ |
|---|
| 38 | int PINT_state_machine_halt(void) |
|---|
| 39 | { |
|---|
| 40 | return 0; |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | /* Function: PINT_state_machine_terminate |
|---|
| 44 | Params: smcb, job status |
|---|
| 45 | Returns: 0 on sucess, otherwise error |
|---|
| 46 | Synopsis: This function cleans up and terminates a SM |
|---|
| 47 | in some cases we may need to keep the SM alive until |
|---|
| 48 | its children terminate or something - in which case we |
|---|
| 49 | will post the relevant job here. |
|---|
| 50 | */ |
|---|
| 51 | int PINT_state_machine_terminate(struct PINT_smcb *smcb, job_status_s *r) |
|---|
| 52 | { |
|---|
| 53 | struct PINT_frame_s *f; |
|---|
| 54 | void *my_frame; |
|---|
| 55 | job_id_t id; |
|---|
| 56 | |
|---|
| 57 | /* notify parent */ |
|---|
| 58 | if (smcb->parent_smcb) |
|---|
| 59 | { |
|---|
| 60 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 61 | "[SM Terminating Child]: (%p) (error_code: %d)\n", |
|---|
| 62 | smcb, |
|---|
| 63 | /* skip pvfs2_ */ |
|---|
| 64 | (int32_t)r->error_code); |
|---|
| 65 | assert(smcb->parent_smcb->children_running > 0); |
|---|
| 66 | |
|---|
| 67 | my_frame = PINT_sm_frame(smcb, PINT_FRAME_CURRENT); |
|---|
| 68 | /* this will loop from TOS down to the base frame */ |
|---|
| 69 | /* base frame will not be processed */ |
|---|
| 70 | qlist_for_each_entry(f, &smcb->parent_smcb->frames, link) |
|---|
| 71 | { |
|---|
| 72 | if(my_frame == f->frame) |
|---|
| 73 | { |
|---|
| 74 | f->error = r->error_code; |
|---|
| 75 | break; |
|---|
| 76 | } |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | if (--smcb->parent_smcb->children_running <= 0) |
|---|
| 80 | { |
|---|
| 81 | /* no more child state machines running, so we can |
|---|
| 82 | * start up the parent state machine again |
|---|
| 83 | */ |
|---|
| 84 | job_null(0, smcb->parent_smcb, 0, r, &id, smcb->context); |
|---|
| 85 | } |
|---|
| 86 | return SM_ACTION_DEFERRED; |
|---|
| 87 | } |
|---|
| 88 | /* call state machine completion function */ |
|---|
| 89 | if (smcb->terminate_fn) |
|---|
| 90 | { |
|---|
| 91 | (*smcb->terminate_fn)(smcb, r); |
|---|
| 92 | } |
|---|
| 93 | return 0; |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | /* Function: PINT_state_machine_invoke |
|---|
| 97 | Params: smcb pointer and job status pointer |
|---|
| 98 | Returns: return value of state action |
|---|
| 99 | Synopsis: runs the current state action, produces debugging |
|---|
| 100 | output if needed, checls return value and takes action |
|---|
| 101 | if needed (sets op_terminate if SM_ACTION_TERMINATED is |
|---|
| 102 | returned) |
|---|
| 103 | */ |
|---|
| 104 | PINT_sm_action PINT_state_machine_invoke(struct PINT_smcb *smcb, |
|---|
| 105 | job_status_s *r) |
|---|
| 106 | { |
|---|
| 107 | PINT_sm_action retval; |
|---|
| 108 | const char * state_name; |
|---|
| 109 | const char * machine_name; |
|---|
| 110 | int children_started = 0; |
|---|
| 111 | |
|---|
| 112 | if (!(smcb) || !(smcb->current_state) || |
|---|
| 113 | !(smcb->current_state->flag == SM_RUN || |
|---|
| 114 | smcb->current_state->flag == SM_PJMP) || |
|---|
| 115 | !(smcb->current_state->action.func)) |
|---|
| 116 | { |
|---|
| 117 | gossip_err("SM invoke called on invalid smcb or state\n"); |
|---|
| 118 | return SM_ERROR; |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | state_name = PINT_state_machine_current_state_name(smcb); |
|---|
| 122 | machine_name = PINT_state_machine_current_machine_name(smcb); |
|---|
| 123 | |
|---|
| 124 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 125 | "[SM Entering]: (%p) %s:%s (status: %d)\n", |
|---|
| 126 | smcb, |
|---|
| 127 | /* skip pvfs2_ */ |
|---|
| 128 | machine_name, |
|---|
| 129 | state_name, |
|---|
| 130 | (int32_t)r->status_user_tag); |
|---|
| 131 | |
|---|
| 132 | /* call state action function */ |
|---|
| 133 | retval = (smcb->current_state->action.func)(smcb,r); |
|---|
| 134 | /* process return code */ |
|---|
| 135 | switch (retval) |
|---|
| 136 | { |
|---|
| 137 | case SM_ACTION_TERMINATE : |
|---|
| 138 | smcb->op_terminate = 1; |
|---|
| 139 | break; |
|---|
| 140 | case SM_ACTION_COMPLETE : |
|---|
| 141 | case SM_ACTION_DEFERRED : |
|---|
| 142 | break; |
|---|
| 143 | default : |
|---|
| 144 | /* error */ |
|---|
| 145 | gossip_err("SM Action %s:%s returned invalid return code %d (%p)\n", |
|---|
| 146 | machine_name, state_name, retval, smcb); |
|---|
| 147 | break; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | /* print post-call debugging info */ |
|---|
| 151 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 152 | "[SM Exiting]: (%p) %s:%s (error code: %d), (action: %s)\n", |
|---|
| 153 | smcb, |
|---|
| 154 | /* skip pvfs2_ */ |
|---|
| 155 | machine_name, |
|---|
| 156 | state_name, |
|---|
| 157 | r->error_code, |
|---|
| 158 | SM_ACTION_STRING(retval)); |
|---|
| 159 | |
|---|
| 160 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 161 | "retval=%d, current_state->flag=%d, children_running=%d\n", |
|---|
| 162 | retval, |
|---|
| 163 | smcb->current_state->flag, |
|---|
| 164 | smcb->children_running); /* sson */ |
|---|
| 165 | if (retval == SM_ACTION_COMPLETE && smcb->current_state->flag == SM_PJMP) |
|---|
| 166 | { |
|---|
| 167 | /* start child SMs */ |
|---|
| 168 | PINT_sm_start_child_frames(smcb, &children_started); |
|---|
| 169 | /* if any children were started, then we return DEFERRED (even |
|---|
| 170 | * though they may have all completed immediately). The last child |
|---|
| 171 | * issues a job_null that will drive progress from here and we don't |
|---|
| 172 | * want to cause a double transition. |
|---|
| 173 | */ |
|---|
| 174 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "smcb=%p: children_started=%d\n", smcb, children_started); |
|---|
| 175 | if (children_started > 0) |
|---|
| 176 | retval = SM_ACTION_DEFERRED; |
|---|
| 177 | else |
|---|
| 178 | retval = SM_ACTION_COMPLETE; |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | return retval; |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | /* Function: PINT_state_machine_start() |
|---|
| 185 | Params: smcb pointer and job status pointer |
|---|
| 186 | Returns: return value of last state action |
|---|
| 187 | Synopsis: Runs the state action pointed to by the |
|---|
| 188 | current state, then continues to run the SM |
|---|
| 189 | as long as return code is SM_ACTION_COMPLETE. |
|---|
| 190 | */ |
|---|
| 191 | |
|---|
| 192 | PINT_sm_action PINT_state_machine_start(struct PINT_smcb *smcb, job_status_s *r) |
|---|
| 193 | { |
|---|
| 194 | PINT_sm_action ret; |
|---|
| 195 | |
|---|
| 196 | /* set the state machine to being completed immediately. We |
|---|
| 197 | * unset this bit once the state machine is deferred. |
|---|
| 198 | */ |
|---|
| 199 | smcb->immediate = 1; |
|---|
| 200 | |
|---|
| 201 | /* set the base frame to be the current TOS, which should be 0 */ |
|---|
| 202 | smcb->base_frame = smcb->frame_count - 1; |
|---|
| 203 | |
|---|
| 204 | /* run the current state action function */ |
|---|
| 205 | ret = PINT_state_machine_invoke(smcb, r); |
|---|
| 206 | if (ret == SM_ACTION_COMPLETE || ret == SM_ACTION_TERMINATE) |
|---|
| 207 | { |
|---|
| 208 | /* keep running until state machine deferrs or terminates */ |
|---|
| 209 | ret = PINT_state_machine_continue(smcb, r); |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | if(ret == SM_ACTION_DEFERRED) |
|---|
| 213 | { |
|---|
| 214 | /* this state machine isn't completing immediately */ |
|---|
| 215 | smcb->immediate = 0; |
|---|
| 216 | } |
|---|
| 217 | |
|---|
| 218 | return ret; |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | /* Function: PINT_state_machine_next() |
|---|
| 222 | Params: smcb pointer and job status pointer |
|---|
| 223 | Returns: return value of last state action |
|---|
| 224 | Synopsis: Runs through a list of return values to find the next function to |
|---|
| 225 | call. Calls that function. If that function returned COMPLETED loop |
|---|
| 226 | and repeat. |
|---|
| 227 | */ |
|---|
| 228 | PINT_sm_action PINT_state_machine_next(struct PINT_smcb *smcb, job_status_s *r) |
|---|
| 229 | { |
|---|
| 230 | int i; /* index for transition table */ |
|---|
| 231 | struct PINT_tran_tbl_s *transtbl; |
|---|
| 232 | PINT_sm_action ret; /* holds state action return code */ |
|---|
| 233 | |
|---|
| 234 | if (!smcb) |
|---|
| 235 | { |
|---|
| 236 | gossip_err("SM next called on invald smcb\n"); |
|---|
| 237 | return -1; |
|---|
| 238 | } |
|---|
| 239 | if(PINT_smcb_cancelled(smcb)) |
|---|
| 240 | { |
|---|
| 241 | return SM_ACTION_TERMINATE; |
|---|
| 242 | } |
|---|
| 243 | |
|---|
| 244 | /* loop while invoke of new state returns COMPLETED */ |
|---|
| 245 | do { |
|---|
| 246 | /* loop while returning from nested SM */ |
|---|
| 247 | do { |
|---|
| 248 | if (!smcb->current_state || !smcb->current_state->trtbl) |
|---|
| 249 | { |
|---|
| 250 | gossip_err("SM current state or trtbl is invalid " |
|---|
| 251 | "(smcb = %p)\n", smcb); |
|---|
| 252 | gossip_backtrace(); |
|---|
| 253 | assert(0); |
|---|
| 254 | return -1; |
|---|
| 255 | } |
|---|
| 256 | transtbl = smcb->current_state->trtbl; |
|---|
| 257 | |
|---|
| 258 | /* for each entry in the transition table there is a return |
|---|
| 259 | * code followed by a next state pointer to the new state. |
|---|
| 260 | * This loops through each entry, checking for a match on the |
|---|
| 261 | * return address, and then sets the new current_state and calls |
|---|
| 262 | * the new state action function */ |
|---|
| 263 | for (i = 0; transtbl[i].return_value != DEFAULT_ERROR; i++) |
|---|
| 264 | { |
|---|
| 265 | if (transtbl[i].return_value == r->error_code) |
|---|
| 266 | break; |
|---|
| 267 | } |
|---|
| 268 | /* we expect the last state action function to return |
|---|
| 269 | * SM_ACTION_TERMINATE which sets the smcb->op_terminate |
|---|
| 270 | * flag. ALSO the state machine must direct the next state |
|---|
| 271 | * to be terminate, which sets loc->flag to SM_TERMINATE. |
|---|
| 272 | * We'll terminate for EITHER, but print an error if not |
|---|
| 273 | * both. |
|---|
| 274 | */ |
|---|
| 275 | if(transtbl[i].flag == SM_TERM || smcb->op_terminate) |
|---|
| 276 | { |
|---|
| 277 | if (!(transtbl[i].flag == SM_TERM)) |
|---|
| 278 | { |
|---|
| 279 | gossip_lerr("Error: state machine returned" |
|---|
| 280 | " SM_ACTION_TERMINATE but didn't reach terminate\n"); |
|---|
| 281 | } |
|---|
| 282 | if (!smcb->op_terminate) |
|---|
| 283 | { |
|---|
| 284 | gossip_lerr("Error: state machine reached terminate" |
|---|
| 285 | " without returning SM_ACTION_TERMINATE\n"); |
|---|
| 286 | smcb->op_terminate = 1; |
|---|
| 287 | } |
|---|
| 288 | return SM_ACTION_TERMINATE; |
|---|
| 289 | } |
|---|
| 290 | if (transtbl[i].flag == SM_RETURN) |
|---|
| 291 | { |
|---|
| 292 | /* if this is a return pop the stack |
|---|
| 293 | * and we'll continue from the state returned to |
|---|
| 294 | */ |
|---|
| 295 | smcb->current_state = PINT_pop_state(smcb); |
|---|
| 296 | if(!smcb->current_state || |
|---|
| 297 | smcb->current_state->trtbl[0].flag == SM_TERM) |
|---|
| 298 | { |
|---|
| 299 | /* assume nested state machine was invoked without |
|---|
| 300 | * a parent */ |
|---|
| 301 | return SM_ACTION_TERMINATE; |
|---|
| 302 | } |
|---|
| 303 | } |
|---|
| 304 | } while (transtbl[i].flag == SM_RETURN); |
|---|
| 305 | smcb->current_state = transtbl[i].next_state; |
|---|
| 306 | /* To do nested states, we check to see if the next state is |
|---|
| 307 | * a nested state machine, and if so we push the return state |
|---|
| 308 | * onto a stack */ |
|---|
| 309 | while (smcb->current_state->flag == SM_JUMP) |
|---|
| 310 | { |
|---|
| 311 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: SM_JUMP\n", __func__); |
|---|
| 312 | PINT_push_state(smcb, smcb->current_state); |
|---|
| 313 | smcb->current_state = |
|---|
| 314 | smcb->current_state->action.nested->first_state; |
|---|
| 315 | } |
|---|
| 316 | /* runs state_action and returns the return code */ |
|---|
| 317 | ret = PINT_state_machine_invoke(smcb, r); |
|---|
| 318 | } while (ret == SM_ACTION_COMPLETE || ret == SM_ACTION_TERMINATE); |
|---|
| 319 | return ret; |
|---|
| 320 | } |
|---|
| 321 | |
|---|
| 322 | /* Function: PINT_state_machine_continue |
|---|
| 323 | Params: smcb pointer and job status pointer |
|---|
| 324 | Returns: return value of last state action |
|---|
| 325 | Synopsis: This function essentially calls next, and if the state |
|---|
| 326 | machine terminates, calls terminate to perform cleanup. |
|---|
| 327 | This allows separation from the start call (which calls |
|---|
| 328 | next but does not call terminate if the state machine |
|---|
| 329 | terminates). |
|---|
| 330 | */ |
|---|
| 331 | PINT_sm_action PINT_state_machine_continue(struct PINT_smcb *smcb, job_status_s *r) |
|---|
| 332 | { |
|---|
| 333 | PINT_sm_action ret; |
|---|
| 334 | |
|---|
| 335 | ret = PINT_state_machine_next(smcb, r); |
|---|
| 336 | |
|---|
| 337 | if(ret == SM_ACTION_TERMINATE) |
|---|
| 338 | { |
|---|
| 339 | /* process terminating SM */ |
|---|
| 340 | PINT_state_machine_terminate(smcb, r); |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | return ret; |
|---|
| 344 | } |
|---|
| 345 | |
|---|
| 346 | /* Function: PINT_state_machine_locate(void) |
|---|
| 347 | Params: smcb pointer with op correctly set |
|---|
| 348 | Returns: 1 on successful locate, 0 on locate failure, <0 on error |
|---|
| 349 | Synopsis: This function locates the state associated with the op |
|---|
| 350 | specified in smcb->op in order to start a state machine's |
|---|
| 351 | execution. |
|---|
| 352 | */ |
|---|
| 353 | int PINT_state_machine_locate(struct PINT_smcb *smcb) |
|---|
| 354 | { |
|---|
| 355 | struct PINT_state_s *current_tmp; |
|---|
| 356 | struct PINT_state_machine_s *op_sm; |
|---|
| 357 | const char *state_name; |
|---|
| 358 | const char *machine_name; |
|---|
| 359 | |
|---|
| 360 | /* check for valid inputs */ |
|---|
| 361 | if (!smcb || smcb->op < 0 || !smcb->op_get_state_machine) |
|---|
| 362 | { |
|---|
| 363 | gossip_err("State machine requested not valid\n"); |
|---|
| 364 | return -PVFS_EINVAL; |
|---|
| 365 | } |
|---|
| 366 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 367 | "[SM Locating]: (%p) op-id: %d\n",smcb,(smcb)->op); |
|---|
| 368 | /* this is a the usage dependant routine to look up the SM */ |
|---|
| 369 | op_sm = (*smcb->op_get_state_machine)(smcb->op); |
|---|
| 370 | if (op_sm != NULL) |
|---|
| 371 | { |
|---|
| 372 | current_tmp = op_sm->first_state; |
|---|
| 373 | /* handle the case in which the first state points to a nested |
|---|
| 374 | * machine, rather than a simple function |
|---|
| 375 | */ |
|---|
| 376 | while(current_tmp->flag == SM_JUMP) |
|---|
| 377 | { |
|---|
| 378 | PINT_push_state(smcb, current_tmp); |
|---|
| 379 | current_tmp = ((struct PINT_state_machine_s *) |
|---|
| 380 | current_tmp->action.nested)->first_state; |
|---|
| 381 | } |
|---|
| 382 | smcb->current_state = current_tmp; |
|---|
| 383 | |
|---|
| 384 | state_name = PINT_state_machine_current_state_name(smcb); |
|---|
| 385 | machine_name = PINT_state_machine_current_machine_name(smcb); |
|---|
| 386 | |
|---|
| 387 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 388 | "[SM Locating]: (%p) located: %s:%s\n", |
|---|
| 389 | smcb, machine_name, state_name); |
|---|
| 390 | |
|---|
| 391 | return 1; /* indicates successful locate */ |
|---|
| 392 | } |
|---|
| 393 | gossip_err("State machine not found for operation %d\n",smcb->op); |
|---|
| 394 | return 0; /* indicates failed to locate */ |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | /* Function: PINT_smcb_set_op |
|---|
| 398 | Params: pointer to an smcb pointer, and an op code (int) |
|---|
| 399 | Returns: nothing |
|---|
| 400 | Synopsis: sets op on existing smcb and reruns locate if |
|---|
| 401 | we have a valid locate func |
|---|
| 402 | */ |
|---|
| 403 | int PINT_smcb_set_op(struct PINT_smcb *smcb, int op) |
|---|
| 404 | { |
|---|
| 405 | smcb->op = op; |
|---|
| 406 | return PINT_state_machine_locate(smcb); |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | int PINT_smcb_immediate_completion(struct PINT_smcb *smcb) |
|---|
| 410 | { |
|---|
| 411 | return smcb->immediate; |
|---|
| 412 | } |
|---|
| 413 | |
|---|
| 414 | /* Function: PINT_smcb_op |
|---|
| 415 | Params: pointer to an smcb pointer |
|---|
| 416 | Returns: op (int) |
|---|
| 417 | Synopsis: returns the op currently set in the smcb |
|---|
| 418 | */ |
|---|
| 419 | int PINT_smcb_op(struct PINT_smcb *smcb) |
|---|
| 420 | { |
|---|
| 421 | return smcb->op; |
|---|
| 422 | } |
|---|
| 423 | |
|---|
| 424 | static int PINT_smcb_sys_op(struct PINT_smcb *smcb) |
|---|
| 425 | { |
|---|
| 426 | if (smcb->op > 0 && smcb->op < PVFS_OP_SYS_MAXVALID) |
|---|
| 427 | return 1; |
|---|
| 428 | return 0; |
|---|
| 429 | } |
|---|
| 430 | |
|---|
| 431 | static int PINT_smcb_mgmt_op(struct PINT_smcb *smcb) |
|---|
| 432 | { |
|---|
| 433 | if (smcb->op > PVFS_OP_SYS_MAXVAL && smcb->op < PVFS_OP_MGMT_MAXVALID) |
|---|
| 434 | return 1; |
|---|
| 435 | return 0; |
|---|
| 436 | } |
|---|
| 437 | |
|---|
| 438 | static int PINT_smcb_misc_op(struct PINT_smcb *smcb) |
|---|
| 439 | { |
|---|
| 440 | return smcb->op == PVFS_SERVER_GET_CONFIG |
|---|
| 441 | || smcb->op == PVFS_SERVER_FETCH_CONFIG |
|---|
| 442 | || smcb->op == PVFS_CLIENT_JOB_TIMER |
|---|
| 443 | || smcb->op == PVFS_CLIENT_PERF_COUNT_TIMER |
|---|
| 444 | || smcb->op == PVFS_DEV_UNEXPECTED; |
|---|
| 445 | } |
|---|
| 446 | |
|---|
| 447 | int PINT_smcb_invalid_op(struct PINT_smcb *smcb) |
|---|
| 448 | { |
|---|
| 449 | if (!PINT_smcb_sys_op(smcb) && !PINT_smcb_mgmt_op(smcb) && !PINT_smcb_misc_op(smcb)) |
|---|
| 450 | return 1; |
|---|
| 451 | return 0; |
|---|
| 452 | } |
|---|
| 453 | |
|---|
| 454 | /* Function: PINT_smcb_set_complete |
|---|
| 455 | Params: pointer to an smcb pointer |
|---|
| 456 | Returns: nothing |
|---|
| 457 | Synopsis: sets op_terminate on existing smcb |
|---|
| 458 | */ |
|---|
| 459 | void PINT_smcb_set_complete(struct PINT_smcb *smcb) |
|---|
| 460 | { |
|---|
| 461 | smcb->op_terminate = 1; |
|---|
| 462 | } |
|---|
| 463 | |
|---|
| 464 | /* Function: PINT_smcb_complete |
|---|
| 465 | Params: pointer to an smcb pointer |
|---|
| 466 | Returns: op (int) |
|---|
| 467 | Synopsis: returns the op_terminate currently set in the smcb |
|---|
| 468 | */ |
|---|
| 469 | int PINT_smcb_complete(struct PINT_smcb *smcb) |
|---|
| 470 | { |
|---|
| 471 | return smcb->op_terminate; |
|---|
| 472 | } |
|---|
| 473 | |
|---|
| 474 | /* Function: PINT_smcb_set_cancelled |
|---|
| 475 | Params: pointer to an smcb pointer |
|---|
| 476 | Returns: nothing |
|---|
| 477 | Synopsis: sets op_cancelled on existing smcb |
|---|
| 478 | */ |
|---|
| 479 | void PINT_smcb_set_cancelled(struct PINT_smcb *smcb) |
|---|
| 480 | { |
|---|
| 481 | smcb->op_cancelled = 1; |
|---|
| 482 | } |
|---|
| 483 | |
|---|
| 484 | /* Function: PINT_smcb_cancelled |
|---|
| 485 | Params: pointer to an smcb pointer |
|---|
| 486 | Returns: op (int) |
|---|
| 487 | Synopsis: returns the op_cancelled currently set in the smcb |
|---|
| 488 | */ |
|---|
| 489 | int PINT_smcb_cancelled(struct PINT_smcb *smcb) |
|---|
| 490 | { |
|---|
| 491 | return smcb->op_cancelled; |
|---|
| 492 | } |
|---|
| 493 | |
|---|
| 494 | /* Function: PINT_smcb_alloc |
|---|
| 495 | Params: pointer to an smcb pointer, an op code (int), size of frame |
|---|
| 496 | (int), pinter to function to locate SM |
|---|
| 497 | Returns: nothing, but fills in pointer argument |
|---|
| 498 | Synopsis: this allocates an smcb struct, including its frame stack |
|---|
| 499 | and sets the op code so you can start the state machine |
|---|
| 500 | */ |
|---|
| 501 | int PINT_smcb_alloc( |
|---|
| 502 | struct PINT_smcb **smcb, |
|---|
| 503 | int op, |
|---|
| 504 | int frame_size, |
|---|
| 505 | struct PINT_state_machine_s *(*getmach)(int), |
|---|
| 506 | int (*term_fn)(struct PINT_smcb *, job_status_s *), |
|---|
| 507 | job_context_id context_id) |
|---|
| 508 | { |
|---|
| 509 | *smcb = (struct PINT_smcb *)malloc(sizeof(struct PINT_smcb)); |
|---|
| 510 | if (!(*smcb)) |
|---|
| 511 | { |
|---|
| 512 | return -PVFS_ENOMEM; |
|---|
| 513 | } |
|---|
| 514 | /* zero out all members */ |
|---|
| 515 | memset(*smcb, 0, sizeof(struct PINT_smcb)); |
|---|
| 516 | |
|---|
| 517 | INIT_QLIST_HEAD(&(*smcb)->frames); |
|---|
| 518 | (*smcb)->base_frame = -1; /* no frames yet */ |
|---|
| 519 | (*smcb)->frame_count = 0; |
|---|
| 520 | |
|---|
| 521 | /* if frame_size given, allocate a frame */ |
|---|
| 522 | if (frame_size > 0) |
|---|
| 523 | { |
|---|
| 524 | void *new_frame = malloc(frame_size); |
|---|
| 525 | if (!new_frame) |
|---|
| 526 | { |
|---|
| 527 | free(*smcb); |
|---|
| 528 | *smcb = NULL; |
|---|
| 529 | return -PVFS_ENOMEM; |
|---|
| 530 | } |
|---|
| 531 | /* zero out all members */ |
|---|
| 532 | memset(new_frame, 0, frame_size); |
|---|
| 533 | PINT_sm_push_frame(*smcb, 0, new_frame); |
|---|
| 534 | (*smcb)->base_frame = 0; |
|---|
| 535 | } |
|---|
| 536 | (*smcb)->op = op; |
|---|
| 537 | (*smcb)->op_get_state_machine = getmach; |
|---|
| 538 | (*smcb)->terminate_fn = term_fn; |
|---|
| 539 | (*smcb)->context = context_id; |
|---|
| 540 | /* if a getmach given, lookup state machine */ |
|---|
| 541 | if (getmach) |
|---|
| 542 | return PINT_state_machine_locate(*smcb); |
|---|
| 543 | return 0; /* success */ |
|---|
| 544 | } |
|---|
| 545 | |
|---|
| 546 | /* Function: PINT_smcb_free |
|---|
| 547 | * Params: pointer to an smcb pointer |
|---|
| 548 | * Returns: nothing, but sets the pointer to NULL |
|---|
| 549 | * Synopsis: this frees an smcb struct, including |
|---|
| 550 | * anything on the frame stack with a zero task_id |
|---|
| 551 | */ |
|---|
| 552 | void PINT_smcb_free(struct PINT_smcb *smcb) |
|---|
| 553 | { |
|---|
| 554 | struct PINT_frame_s *frame_entry, *tmp; |
|---|
| 555 | assert(smcb); |
|---|
| 556 | qlist_for_each_entry_safe(frame_entry, tmp, &smcb->frames, link) |
|---|
| 557 | { |
|---|
| 558 | if (frame_entry->frame && frame_entry->task_id == 0) |
|---|
| 559 | { |
|---|
| 560 | /* only free if task_id is 0 */ |
|---|
| 561 | free(frame_entry->frame); |
|---|
| 562 | } |
|---|
| 563 | qlist_del(&frame_entry->link); |
|---|
| 564 | free(frame_entry); |
|---|
| 565 | } |
|---|
| 566 | free(smcb); |
|---|
| 567 | } |
|---|
| 568 | |
|---|
| 569 | /* Function: PINT_pop_state |
|---|
| 570 | * Params: pointer to an smcb pointer |
|---|
| 571 | * Returns: |
|---|
| 572 | * Synopsis: pops a SM pointer off of a stack for |
|---|
| 573 | * implementing nested SMs - called by the |
|---|
| 574 | * "next" routine above |
|---|
| 575 | */ |
|---|
| 576 | static struct PINT_state_s *PINT_pop_state(struct PINT_smcb *smcb) |
|---|
| 577 | { |
|---|
| 578 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 579 | "[SM pop_state]: (%p) op-id: %d stk-ptr: %d base-frm: %d\n", |
|---|
| 580 | smcb, smcb->op, smcb->stackptr, smcb->base_frame); |
|---|
| 581 | |
|---|
| 582 | if(smcb->stackptr == 0) |
|---|
| 583 | { |
|---|
| 584 | /* this is not an error, we terminate if we return NULL */ |
|---|
| 585 | /* this is return from main */ |
|---|
| 586 | return NULL; |
|---|
| 587 | } |
|---|
| 588 | |
|---|
| 589 | smcb->stackptr--; |
|---|
| 590 | smcb->base_frame = smcb->state_stack[smcb->stackptr].prev_base_frame; |
|---|
| 591 | return smcb->state_stack[smcb->stackptr].state; |
|---|
| 592 | } |
|---|
| 593 | |
|---|
| 594 | /* Function: PINT_push_state |
|---|
| 595 | * Params: pointer to an smcb pointer |
|---|
| 596 | * Returns: |
|---|
| 597 | * Synopsis: pushes a SM pointer into a stack for |
|---|
| 598 | * implementing nested SMs - called by the |
|---|
| 599 | * "next" routine above |
|---|
| 600 | */ |
|---|
| 601 | static void PINT_push_state(struct PINT_smcb *smcb, |
|---|
| 602 | struct PINT_state_s *p) |
|---|
| 603 | { |
|---|
| 604 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 605 | "[SM push_state]: (%p) op-id: %d stk-ptr: %d base-frm: %d\n", |
|---|
| 606 | smcb, smcb->op, smcb->stackptr, smcb->base_frame); |
|---|
| 607 | |
|---|
| 608 | assert(smcb->stackptr < PINT_STATE_STACK_SIZE); |
|---|
| 609 | |
|---|
| 610 | smcb->state_stack[smcb->stackptr].prev_base_frame = smcb->base_frame; |
|---|
| 611 | smcb->base_frame = smcb->frame_count - 1; |
|---|
| 612 | smcb->state_stack[smcb->stackptr].state = p; |
|---|
| 613 | smcb->stackptr++; |
|---|
| 614 | } |
|---|
| 615 | |
|---|
| 616 | /* Function: PINT_sm_frame |
|---|
| 617 | * Params: pointer to smcb, stack index |
|---|
| 618 | * Returns: pointer to frame |
|---|
| 619 | * Synopsis: returns a frame off of the frame stack |
|---|
| 620 | * An index of 0 indicates the base frame specified in the SMCB |
|---|
| 621 | * A +'ve index indicates a frame pushed by this SM |
|---|
| 622 | * A -'ve index indicates a frame from a prior SM |
|---|
| 623 | * smcb->frames.next is the top of stack |
|---|
| 624 | * smcb->frames.prev is the bottom of stack |
|---|
| 625 | */ |
|---|
| 626 | void *PINT_sm_frame(struct PINT_smcb *smcb, int index) |
|---|
| 627 | { |
|---|
| 628 | struct PINT_frame_s *frame_entry; |
|---|
| 629 | struct qlist_head *prev; |
|---|
| 630 | int target = smcb->base_frame + index; |
|---|
| 631 | |
|---|
| 632 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 633 | "[SM frame get]: (%p) op-id: %d index: %d base-frm: %d\n", |
|---|
| 634 | smcb, smcb->op, index, smcb->base_frame); |
|---|
| 635 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 636 | "children_running=%d\n", smcb->children_running); /* sson */ |
|---|
| 637 | |
|---|
| 638 | if(qlist_empty(&smcb->frames)) |
|---|
| 639 | { |
|---|
| 640 | gossip_err("FRAME GET smcb %p index %d target %d -> List empty\n", |
|---|
| 641 | smcb, index, target); |
|---|
| 642 | return NULL; |
|---|
| 643 | } |
|---|
| 644 | else |
|---|
| 645 | { |
|---|
| 646 | /* target should be 0 .. frame_count-1 now */ |
|---|
| 647 | if (target < 0 || target >= smcb->frame_count) |
|---|
| 648 | { |
|---|
| 649 | gossip_err("FRAME GET smcb %p index %d target %d -> Out of range\n", |
|---|
| 650 | smcb, index, target); |
|---|
| 651 | return NULL; |
|---|
| 652 | } |
|---|
| 653 | prev = smcb->frames.prev; |
|---|
| 654 | while(target) |
|---|
| 655 | { |
|---|
| 656 | target--; |
|---|
| 657 | prev = prev->prev; |
|---|
| 658 | } |
|---|
| 659 | frame_entry = qlist_entry(prev, struct PINT_frame_s, link); |
|---|
| 660 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "returned frame=%p\n", |
|---|
| 661 | frame_entry->frame); /* sson */ |
|---|
| 662 | return frame_entry->frame; |
|---|
| 663 | } |
|---|
| 664 | } |
|---|
| 665 | |
|---|
| 666 | /* Function: PINT_sm_push_frame |
|---|
| 667 | * Params: pointer to smcb, void pointer for new frame |
|---|
| 668 | * Returns: |
|---|
| 669 | * Synopsis: pushes a new frame pointer onto the frame_stack |
|---|
| 670 | */ |
|---|
| 671 | int PINT_sm_push_frame(struct PINT_smcb *smcb, int task_id, void *frame_p) |
|---|
| 672 | { |
|---|
| 673 | struct PINT_frame_s *newframe; |
|---|
| 674 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 675 | "[SM Frame PUSH]: (%p) frame: %p\n", |
|---|
| 676 | smcb, frame_p); |
|---|
| 677 | newframe = malloc(sizeof(struct PINT_frame_s)); |
|---|
| 678 | if(!newframe) |
|---|
| 679 | { |
|---|
| 680 | return -PVFS_ENOMEM; |
|---|
| 681 | } |
|---|
| 682 | newframe->task_id = task_id; |
|---|
| 683 | newframe->frame = frame_p; |
|---|
| 684 | newframe->error = 0; |
|---|
| 685 | qlist_add(&newframe->link, &smcb->frames); |
|---|
| 686 | smcb->frame_count++; |
|---|
| 687 | return 0; |
|---|
| 688 | } |
|---|
| 689 | |
|---|
| 690 | /* Function: PINT_sm_pop_frame |
|---|
| 691 | * Params: smcb - pointer to an smcb pointer |
|---|
| 692 | * task_id - the task id of this frame |
|---|
| 693 | * error_code - the frame's error if there was one. |
|---|
| 694 | * remaining - count of remaining frames on the smcb. |
|---|
| 695 | * Returns: frame pointer |
|---|
| 696 | * Synopsis: pops a frame pointer from the frame_stack and returns it |
|---|
| 697 | */ |
|---|
| 698 | void *PINT_sm_pop_frame(struct PINT_smcb *smcb, |
|---|
| 699 | int *task_id, |
|---|
| 700 | int *error_code, |
|---|
| 701 | int *remaining) |
|---|
| 702 | { |
|---|
| 703 | struct PINT_frame_s *frame_entry; |
|---|
| 704 | void *frame; |
|---|
| 705 | |
|---|
| 706 | if(qlist_empty(&smcb->frames)) |
|---|
| 707 | { |
|---|
| 708 | return NULL; |
|---|
| 709 | } |
|---|
| 710 | |
|---|
| 711 | frame_entry = qlist_entry(smcb->frames.next, struct PINT_frame_s, link); |
|---|
| 712 | qlist_del(smcb->frames.next); |
|---|
| 713 | smcb->frame_count--; |
|---|
| 714 | |
|---|
| 715 | if(remaining) |
|---|
| 716 | { |
|---|
| 717 | *remaining = smcb->frame_count; |
|---|
| 718 | } |
|---|
| 719 | |
|---|
| 720 | frame = frame_entry->frame; |
|---|
| 721 | *error_code = frame_entry->error; |
|---|
| 722 | *task_id = frame_entry->task_id; |
|---|
| 723 | |
|---|
| 724 | free(frame_entry); |
|---|
| 725 | |
|---|
| 726 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, |
|---|
| 727 | "[SM Frame POP]: (%p) frame: %p\n", |
|---|
| 728 | smcb, frame); |
|---|
| 729 | return frame; |
|---|
| 730 | } |
|---|
| 731 | |
|---|
| 732 | /* Function: PINT_sm_task_map |
|---|
| 733 | * Params: smcb and an integer task_id |
|---|
| 734 | * Returns: The state machine a new child state should execute |
|---|
| 735 | * Synopsis: Uses the task_id and task jump table from the SM |
|---|
| 736 | * code to decide which SM a new child should run. Called |
|---|
| 737 | * by the start_child_frames function |
|---|
| 738 | */ |
|---|
| 739 | static struct PINT_state_s *PINT_sm_task_map(struct PINT_smcb *smcb, int task_id) |
|---|
| 740 | { |
|---|
| 741 | struct PINT_pjmp_tbl_s *pjmptbl; |
|---|
| 742 | int i; |
|---|
| 743 | |
|---|
| 744 | pjmptbl = smcb->current_state->pjtbl; |
|---|
| 745 | for (i = 0; ; i++) |
|---|
| 746 | { if (pjmptbl[i].return_value == task_id || |
|---|
| 747 | pjmptbl[i].return_value == -1) |
|---|
| 748 | return pjmptbl[i].state_machine->first_state; |
|---|
| 749 | } |
|---|
| 750 | } |
|---|
| 751 | |
|---|
| 752 | static int child_sm_frame_terminate(struct PINT_smcb * smcb, job_status_s * js_p) |
|---|
| 753 | { |
|---|
| 754 | PINT_smcb_free(smcb); |
|---|
| 755 | return 0; |
|---|
| 756 | } |
|---|
| 757 | |
|---|
| 758 | /* Function: PINT_sm_start_child_frames |
|---|
| 759 | * Params: pointer to an smcb pointer and pointer to count of children |
|---|
| 760 | * started |
|---|
| 761 | * Returns: number of children started |
|---|
| 762 | * Synopsis: This starts all the enw child SMs based on the frame_stack |
|---|
| 763 | * This is called by the invoke function above which expects the |
|---|
| 764 | * number of children to be returned to decide if the state is |
|---|
| 765 | * deferred or not. |
|---|
| 766 | */ |
|---|
| 767 | static void PINT_sm_start_child_frames(struct PINT_smcb *smcb, int* children_started) |
|---|
| 768 | { |
|---|
| 769 | int retval; |
|---|
| 770 | struct PINT_smcb *new_sm; |
|---|
| 771 | job_status_s r; |
|---|
| 772 | struct PINT_frame_s *f; |
|---|
| 773 | void *my_frame; |
|---|
| 774 | |
|---|
| 775 | assert(smcb); |
|---|
| 776 | |
|---|
| 777 | memset(&r, 0, sizeof(job_status_s)); |
|---|
| 778 | |
|---|
| 779 | *children_started = 0; |
|---|
| 780 | |
|---|
| 781 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: called\n", __func__); /* sson */ |
|---|
| 782 | my_frame = PINT_sm_frame(smcb, PINT_FRAME_CURRENT); |
|---|
| 783 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: my_frame=%p\n", __func__, my_frame); /* sson */ |
|---|
| 784 | /* Iterate once up front to determine how many children we are going to |
|---|
| 785 | * run. This has to be set before starting any children, otherwise if |
|---|
| 786 | * the first one immediately completes it will mistakenly believe it is |
|---|
| 787 | * the last one and signal the parent. |
|---|
| 788 | */ |
|---|
| 789 | qlist_for_each_entry(f, &smcb->frames, link) |
|---|
| 790 | { |
|---|
| 791 | gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "f->frame=%p\n", f->frame); |
|---|
| 792 | /* run from TOS until the parent frame */ |
|---|
| 793 | if(f->frame == my_frame) |
|---|
| 794 | { |
|---|
| 795 | break; |
|---|
| 796 | } |
|---|
| 797 | /* increment parent's counter */ |
|---|
| 798 | smcb->children_running++; |
|---|
| 799 | } |
|---|
| 800 | |
|---|
| 801 | /* let the caller know how many children are being started; it won't be |
|---|
| 802 | * able to tell from the running_count because they may all immediately |
|---|
| 803 | * complete before we leave this function. |
|---|
| 804 | */ |
|---|
| 805 | *children_started = smcb->children_running; |
|---|
| 806 | |
|---|
| 807 | qlist_for_each_entry(f, &smcb->frames, link) |
|---|
| 808 | { |
|---|
| 809 | /* run from TOS until the parent frame */ |
|---|
| 810 | if(f->frame == my_frame) |
|---|
| 811 | { |
|---|
| 812 | break; |
|---|
| 813 | } |
|---|
| 814 | /* allocate smcb */ |
|---|
| 815 | PINT_smcb_alloc(&new_sm, smcb->op, 0, NULL, |
|---|
| 816 | child_sm_frame_terminate, smcb->context); |
|---|
| 817 | /* set parent smcb pointer */ |
|---|
| 818 | new_sm->parent_smcb = smcb; |
|---|
| 819 | /* assign frame */ |
|---|
| 820 | PINT_sm_push_frame(new_sm, f->task_id, f->frame); |
|---|
| 821 | /* locate SM to run */ |
|---|
| 822 | new_sm->current_state = PINT_sm_task_map(smcb, f->task_id); |
|---|
| 823 | /* invoke SM */ |
|---|
| 824 | retval = PINT_state_machine_start(new_sm, &r); |
|---|
| 825 | if(retval < 0) |
|---|
| 826 | { |
|---|
| 827 | gossip_err("PJMP child state machine failed to start.\n"); |
|---|
| 828 | } |
|---|
| 829 | } |
|---|
| 830 | } |
|---|
| 831 | |
|---|
| 832 | char * PINT_sm_action_string[3] = |
|---|
| 833 | { |
|---|
| 834 | "DEFERRED", |
|---|
| 835 | "COMPLETE", |
|---|
| 836 | "TERMINATE" |
|---|
| 837 | }; |
|---|
| 838 | |
|---|
| 839 | /* |
|---|
| 840 | * Local variables: |
|---|
| 841 | * c-indent-level: 4 |
|---|
| 842 | * c-basic-offset: 4 |
|---|
| 843 | * End: |
|---|
| 844 | * |
|---|
| 845 | * vim: ts=8 sts=4 sw=4 expandtab |
|---|
| 846 | */ |
|---|
| 847 | |
|---|
| 848 | #endif |
|---|
| 849 | |
|---|