1 /* -*- Mode: C; c-basic-offset:4 ; -*- */ 2 /* 3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana 4 * University Research and Technology 5 * Corporation. All rights reserved. 6 * Copyright (c) 2004-2005 The University of Tennessee and The University 7 * of Tennessee Research Foundation. All rights 8 * reserved. 9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 10 * University of Stuttgart. All rights reserved. 11 * Copyright (c) 2004-2005 The Regents of the University of California. 12 * All rights reserved. 13 * Copyright (c) 2012 Los Alamos National Security, Inc. All rights reserved. 14 * Copyright (c) 2013-2018 Intel, Inc. All rights reserved. 15 * Copyright (c) 2015 Research Organization for Information Science 16 * and Technology (RIST). All rights reserved. 17 * Copyright (c) 2016 Mellanox Technologies, Inc. 18 * All rights reserved. 19 * $COPYRIGHT$ 20 * 21 * Additional copyrights may follow 22 * 23 * $HEADER$ 24 */ 25 /** 26 * @file 27 * 28 * Data packing subsystem. 29 */ 30 31 #ifndef PMIX_BFROP_H_ 32 #define PMIX_BFROP_H_ 33 34 #include <src/include/pmix_config.h> 35 #include <pmix_common.h> 36 #include <src/include/types.h> 37 38 #include "src/mca/mca.h" 39 40 #include "bfrops_types.h" 41 42 BEGIN_C_DECLS 43 44 /* The overall objective of this framework is to provide seamless 45 * cross-version support for communications by allowing a process 46 * to communicate with a peer: 47 * 48 * (a) using a different version of the buffer operations. We are 49 * allowing changes in the structure compositions and/or data 50 * type definitions between versions. This specifically affects 51 * our ability to pack/unpack across versions. 52 * 53 * (b) using a different buffer type (described vs non-described). 54 * This resolves conflicts when one side was compiled with a 55 * debug option, while the other side has been "optimized". 56 * 57 * This is a mult-select framework - i.e., multiple components 58 * are selected and "active" at the same time. The intent is 59 * to have one component for each data type variation, with the 60 * expectation that the community will do its best not to revise 61 * existing data type definitions. Thus, new variations should be 62 * rare, and only a few components will exist. 63 * 64 * The framework itself reflects the fact that any given peer 65 * will utilize only one variation of the data type definitions. 66 * Thus, once a peer is identified, it will pass its version string 67 * to this framework's "assign_module" function, which will then 68 * pass it to each component until one returns a module capable of 69 * processing the given version. This module is then "attached" to 70 * the pmix_peer_t object so it can be used for all subsequent 71 * communication to/from that peer. 72 * 73 * Buffer type is included in the buffer metadata. Unfortunately, 74 * the metadata is not communicated at each exchange. Thus, the 75 * peers will indicate during the connection handshake the type 76 * of buffer they will use for all subsequent communications. The 77 * peer must then utilize that same buffer type for all messages 78 * sent to that remote proc, so we provide new macros for creating 79 * and constructing buffers that ensures the correct buffer type 80 * is marked. 81 * 82 * Accordingly, there are two levels of APIs defined for this 83 * framework: 84 * 85 * (a) component level - these allow for init/finalize of the 86 * component, and assignment of a module to a given peer 87 * based on the version that peer is using 88 * 89 * (b) module level - implement pack/unpack/copy/recv/etc. of 90 * the various datatypes. Note that the module only needs 91 * to provide those functions that differ from the base 92 * functions - they don't need to duplicate all that code! 93 */ 94 95 96 /* The following functions are exposed to the user - they 97 * therefore are implemented in the bfrops/base functions 98 * as wrappers to the real functions. 99 * 100 * NOTE: THESE FUNCTIONS ARE NOT TO BE USED INTERNALLY - 101 * USE THE MACROS INSTEAD 102 */ 103 bool pmix_value_cmp(pmix_value_t *p, pmix_value_t *p1); 104 105 106 107 /**** MODULE INTERFACE DEFINITION ****/ 108 109 /* initialize the module - the module is expected 110 * to register its datatype functions at this time */ 111 typedef pmix_status_t (*pmix_bfrop_init_fn_t)(void); 112 113 /* finalize the module */ 114 typedef void (*pmix_bfrop_finalize_fn_t)(void); 115 116 /** 117 * Pack one or more values into a buffer. 118 * 119 * The pack function packs one or more values of a specified type into 120 * the specified buffer. The buffer must have already been 121 * initialized via an PMIX_NEW or PMIX_CONSTRUCT call - otherwise, the 122 * pack_value function will return an error. Providing an unsupported 123 * type flag will likewise be reported as an error. 124 * 125 * Note that any data to be packed that is not hard type cast (i.e., 126 * not type cast to a specific size) may lose precision when unpacked 127 * by a non-homogeneous recipient. The BFROP will do its best to deal 128 * with heterogeneity issues between the packer and unpacker in such 129 * cases. Sending a number larger than can be handled by the recipient 130 * will return an error code (generated by the BFROP upon unpacking) - 131 * the BFROP cannot detect such errors during packing. 132 * 133 * @param *buffer A pointer to the buffer into which the value is to 134 * be packed. 135 * 136 * @param *src A void* pointer to the data that is to be packed. This 137 * is interpreted as a pointer to an array of that data type containing 138 * length num_values. Note that strings are of data type char*, and so 139 * they are to be passed as (char **) - i.e., the caller must 140 * pass the address of the pointer to the string as the void*. This 141 * allows the BFROP to use a single interface function, but still allow 142 * the caller to pass multiple strings in a single call. 143 * 144 * @param num_values An int32_t indicating the number of values that are 145 * to be packed, beginning at the location pointed to by src. A string 146 * value is counted as a single value regardless of length. The values 147 * must be contiguous in memory. Arrays of pointers (e.g., string 148 * arrays) should be contiguous, although (obviously) the data pointed 149 * to need not be contiguous across array entries. 150 * 151 * @param type The type of the data to be packed - must be one of the 152 * PMIX defined data types. 153 * 154 * @retval PMIX_SUCCESS The data was packed as requested. 155 * 156 * @retval PMIX_ERROR(s) An appropriate PMIX error code indicating the 157 * problem encountered. This error code should be handled 158 * appropriately. 159 * 160 * @code 161 * pmix_buffer_t *buffer; 162 * int32_t src; 163 * 164 * status_code = pmix_bfrop.pack(buffer, &src, 1, PMIX_INT32); 165 * @endcode 166 */ 167 typedef pmix_status_t (*pmix_bfrop_pack_fn_t)(pmix_buffer_t *buffer, 168 const void *src, 169 int32_t num_values, 170 pmix_data_type_t type); 171 172 /** 173 * Unpack values from a buffer. 174 * 175 * The unpack function unpacks the next value (or values) of a 176 * specified type from the specified buffer. 177 * 178 * The buffer must have already been initialized via an PMIX_NEW or 179 * PMIX_CONSTRUCT call (and assumedly filled with some data) - 180 * otherwise, the unpack_value function will return an 181 * error. Providing an unsupported type flag will likewise be reported 182 * as an error, as will specifying a data type that DOES NOT match the 183 * type of the next item in the buffer. An attempt to read beyond the 184 * end of the stored data held in the buffer will also return an 185 * error. 186 * 187 * NOTE: it is possible for the buffer to be corrupted and that 188 * the BFROP will *think* there is a proper variable type at the 189 * beginning of an unpack region - but that the value is bogus (e.g., just 190 * a byte field in a string array that so happens to have a value that 191 * matches the specified data type flag). Therefore, the data type error check 192 * is NOT completely safe. This is true for ALL unpack functions. 193 * 194 * Warning: The caller is responsible for providing adequate memory 195 * storage for the requested data. As noted below, the user 196 * must provide a parameter indicating the maximum number of values that 197 * can be unpacked into the allocated memory. If more values exist in the 198 * buffer than can fit into the memory storage, then the bfrop will unpack 199 * what it can fit into that location and return an error code indicating 200 * that the buffer was only partially unpacked. 201 * 202 * Note that any data that was not hard type cast (i.e., not type cast 203 * to a specific size) when packed may lose precision when unpacked by 204 * a non-homogeneous recipient. The BFROP will do its best to deal with 205 * heterogeneity issues between the packer and unpacker in such 206 * cases. Sending a number larger than can be handled by the recipient 207 * will return an error code generated by the BFROP upon unpacking - the 208 * BFROP cannot detect such errors during packing. 209 * 210 * @param *buffer A pointer to the buffer from which the value will be 211 * extracted. 212 * 213 * @param *dest A void* pointer to the memory location into which the 214 * data is to be stored. Note that these values will be stored 215 * contiguously in memory. For strings, this pointer must be to (char 216 * **) to provide a means of supporting multiple string 217 * operations. The BFROP unpack function will allocate memory for each 218 * string in the array - the caller must only provide adequate memory 219 * for the array of pointers. 220 * 221 * @param type The type of the data to be unpacked - must be one of 222 * the BFROP defined data types. 223 * 224 * @retval *max_num_values The number of values actually unpacked. In 225 * most cases, this should match the maximum number provided in the 226 * parameters - but in no case will it exceed the value of this 227 * parameter. Note that if you unpack fewer values than are actually 228 * available, the buffer will be in an unpackable state - the bfrop will 229 * return an error code to warn of this condition. 230 * 231 * @note The unpack function will return the actual number of values 232 * unpacked in this location. 233 * 234 * @retval PMIX_SUCCESS The next item in the buffer was successfully 235 * unpacked. 236 * 237 * @retval PMIX_ERROR(s) The unpack function returns an error code 238 * under one of several conditions: (a) the number of values in the 239 * item exceeds the max num provided by the caller; (b) the type of 240 * the next item in the buffer does not match the type specified by 241 * the caller; or (c) the unpack failed due to either an error in the 242 * buffer or an attempt to read past the end of the buffer. 243 * 244 * @code 245 * pmix_buffer_t *buffer; 246 * int32_t dest; 247 * char **string_array; 248 * int32_t num_values; 249 * 250 * num_values = 1; 251 * status_code = pmix_bfrop.unpack(buffer, (void*)&dest, &num_values, PMIX_INT32); 252 * 253 * num_values = 5; 254 * string_array = malloc(num_values*sizeof(char *)); 255 * status_code = pmix_bfrop.unpack(buffer, (void*)(string_array), &num_values, PMIX_STRING); 256 * 257 * @endcode 258 */ 259 typedef pmix_status_t (*pmix_bfrop_unpack_fn_t)(pmix_buffer_t *buffer, void *dest, 260 int32_t *max_num_values, 261 pmix_data_type_t type); 262 /** 263 * Copy a payload from one buffer to another 264 * This function will append a copy of the payload in one buffer into 265 * another buffer. If the destination buffer is NOT empty, then the 266 * type of the two buffers MUST match or else an 267 * error will be returned. If the destination buffer IS empty, then 268 * its type will be set to that of the source buffer. 269 * NOTE: This is NOT a destructive procedure - the 270 * source buffer's payload will remain intact, as will any pre-existing 271 * payload in the destination's buffer. 272 */ 273 typedef pmix_status_t (*pmix_bfrop_copy_payload_fn_t)(pmix_buffer_t *dest, 274 pmix_buffer_t *src); 275 276 /** 277 * Copy a data value from one location to another. 278 * 279 * Since registered data types can be complex structures, the system 280 * needs some way to know how to copy the data from one location to 281 * another (e.g., for storage in the registry). This function, which 282 * can call other copy functions to build up complex data types, defines 283 * the method for making a copy of the specified data type. 284 * 285 * @param **dest The address of a pointer into which the 286 * address of the resulting data is to be stored. 287 * 288 * @param *src A pointer to the memory location from which the 289 * data is to be copied. 290 * 291 * @param type The type of the data to be copied - must be one of 292 * the BFROP defined data types. 293 * 294 * @retval PMIX_SUCCESS The value was successfully copied. 295 * 296 * @retval PMIX_ERROR(s) An appropriate error code. 297 * 298 */ 299 typedef pmix_status_t (*pmix_bfrop_copy_fn_t)(void **dest, void *src, 300 pmix_data_type_t type); 301 302 /** 303 * Print a data value. 304 * 305 * Since registered data types can be complex structures, the system 306 * needs some way to know how to print them (i.e., convert them to a string 307 * representation). Provided for debug purposes. 308 * 309 * @retval PMIX_SUCCESS The value was successfully printed. 310 * 311 * @retval PMIX_ERROR(s) An appropriate error code. 312 */ 313 typedef pmix_status_t (*pmix_bfrop_print_fn_t)(char **output, char *prefix, 314 void *src, pmix_data_type_t type); 315 316 /** 317 * Transfer a value from one pmix_value_t to another. Ordinarily, 318 * this would be executed as a base function. However, it is 319 * possible that future versions may add new data types, and 320 * thus the xfer function may differ 321 * 322 * @retval PMIX_SUCCESS The value was successfully transferred 323 * 324 * @retval PMIX_ERROR(s) An appropriate error code 325 */ 326 typedef pmix_status_t (*pmix_bfrop_value_xfer_fn_t)(pmix_value_t *dest, 327 const pmix_value_t *src); 328 329 330 /** 331 * Load data into a pmix_value_t object. Again, this is provided 332 * as a component function to support different data types 333 */ 334 typedef void (*pmix_bfrop_value_load_fn_t)(pmix_value_t *v, const void *data, 335 pmix_data_type_t type); 336 337 /** 338 * Unload data from a pmix_value_t object 339 * 340 * @retval PMIX_SUCCESS The value was successfully unloaded 341 * 342 * @retval PMIX_ERROR(s) An appropriate error code 343 */ 344 typedef pmix_status_t (*pmix_bfrop_value_unload_fn_t)(pmix_value_t *kv, 345 void **data, size_t *sz); 346 347 /** 348 * Compare two pmix_value_t structs 349 */ 350 typedef pmix_value_cmp_t (*pmix_bfrop_value_cmp_fn_t)(pmix_value_t *p1, pmix_value_t *p2); 351 352 /* define a component-level API for registering a new 353 * datatype, providing all the required functions */ 354 typedef pmix_status_t (*pmix_bfrop_base_register_fn_t)(const char *name, pmix_data_type_t type, 355 pmix_bfrop_pack_fn_t pack, 356 pmix_bfrop_unpack_fn_t unpack, 357 pmix_bfrop_copy_fn_t copy, 358 pmix_bfrop_print_fn_t print); 359 360 /* return the string name of a provided data type */ 361 typedef const char* (*pmix_bfrop_data_type_string_fn_t)(pmix_data_type_t type); 362 363 /** 364 * Base structure for a BFROP module 365 */ 366 typedef struct { 367 char *version; 368 pmix_bfrop_init_fn_t init; 369 pmix_bfrop_finalize_fn_t finalize; 370 pmix_bfrop_pack_fn_t pack; 371 pmix_bfrop_unpack_fn_t unpack; 372 pmix_bfrop_copy_fn_t copy; 373 pmix_bfrop_print_fn_t print; 374 pmix_bfrop_copy_payload_fn_t copy_payload; 375 pmix_bfrop_value_xfer_fn_t value_xfer; 376 pmix_bfrop_value_load_fn_t value_load; 377 pmix_bfrop_value_unload_fn_t value_unload; 378 pmix_bfrop_value_cmp_fn_t value_cmp; 379 pmix_bfrop_base_register_fn_t register_type; 380 pmix_bfrop_data_type_string_fn_t data_type_string; 381 } pmix_bfrops_module_t; 382 383 384 /* get a list of available versions - caller must free results 385 * when done */ 386 PMIX_EXPORT char* pmix_bfrops_base_get_available_modules(void); 387 388 /* Select a bfrops module for a given version */ 389 PMIX_EXPORT pmix_bfrops_module_t* pmix_bfrops_base_assign_module(const char *version); 390 391 /* provide a backdoor to access the framework debug output */ 392 PMIX_EXPORT extern int pmix_bfrops_base_output; 393 394 /* MACROS FOR EXECUTING BFROPS FUNCTIONS */ 395 #define PMIX_BFROPS_ASSIGN_TYPE(p, b) \ 396 (b)->type = (p)->nptr->compat.type 397 398 #define PMIX_BFROPS_PACK(r, p, b, s, n, t) \ 399 do { \ 400 pmix_output_verbose(2, pmix_bfrops_base_output, \ 401 "[%s:%d] PACK version %s", \ 402 __FILE__, __LINE__, \ 403 (p)->nptr->compat.bfrops->version); \ 404 if (PMIX_BFROP_BUFFER_UNDEF == (b)->type) { \ 405 (b)->type = (p)->nptr->compat.type; \ 406 (r) = (p)->nptr->compat.bfrops->pack(b, s, n, t); \ 407 } else if ((b)->type == (p)->nptr->compat.type) { \ 408 (r) = (p)->nptr->compat.bfrops->pack(b, s, n, t); \ 409 } else { \ 410 (r) = PMIX_ERR_PACK_MISMATCH; \ 411 } \ 412 } while(0) 413 414 #define PMIX_BFROPS_UNPACK(r, p, b, d, m, t) \ 415 do { \ 416 pmix_output_verbose(2, pmix_bfrops_base_output, \ 417 "[%s:%d] UNPACK version %s", \ 418 __FILE__, __LINE__, \ 419 (p)->nptr->compat.bfrops->version); \ 420 if ((b)->type == (p)->nptr->compat.type) { \ 421 (r) = (p)->nptr->compat.bfrops->unpack(b, d, m, t); \ 422 } else { \ 423 (r) = PMIX_ERR_UNPACK_FAILURE; \ 424 } \ 425 } while(0) 426 427 #define PMIX_BFROPS_COPY(r, p, d, s, t) \ 428 (r) = (p)->nptr->compat.bfrops->copy(d, s, t) 429 430 #define PMIX_BFROPS_PRINT(r, p, o, pr, s, t) \ 431 (r) = (p)->nptr->compat.bfrops->print(o, pr, s, t) 432 433 #define PMIX_BFROPS_COPY_PAYLOAD(r, p, d, s) \ 434 do { \ 435 if (PMIX_BFROP_BUFFER_UNDEF == (d)->type) { \ 436 (d)->type = (p)->nptr->compat.type; \ 437 (r) = (p)->nptr->compat.bfrops->copy_payload(d, s); \ 438 } else if ((d)->type == (p)->nptr->compat.type) { \ 439 (r) = (p)->nptr->compat.bfrops->copy_payload(d, s); \ 440 } else { \ 441 (r) = PMIX_ERR_PACK_MISMATCH; \ 442 } \ 443 } while(0) 444 445 #define PMIX_BFROPS_VALUE_XFER(r, p, d, s) \ 446 (r) = (p)->nptr->compat.bfrops->value_xfer(d, s) 447 448 #define PMIX_BFROPS_VALUE_LOAD(p, v, d, t) \ 449 (p)->nptr->compat.bfrops->value_load(v, d, t) 450 451 #define PMIX_BFROPS_VALUE_UNLOAD(r, p, k, d, s) \ 452 (r) = (p)->nptr->compat.bfrops->value_unload(k,, d, s) 453 454 #define PMIX_BFROPS_VALUE_CMP(r, p, q, s) \ 455 (r) = (p)->nptr->compat.bfrops->value_cmp(q, s) 456 457 #define PMIX_BFROPS_REGISTER(r, p, n, t, pk, u, c, pr) \ 458 (r) = (p)->nptr->compat.bfrops->register_type(n, t, pk, u, c, pr) 459 460 #define PMIX_BFROPS_PRINT_TYPE(c, p, t) \ 461 (c) = (p)->nptr->compat.bfrops->data_type_string(t) 462 463 464 /**** COMPONENT STRUCTURE DEFINITION ****/ 465 466 /* define a component-level API for getting a module */ 467 typedef pmix_bfrops_module_t* (*pmix_bfrop_base_component_assign_module_fn_t)(void); 468 469 /* 470 * the standard component data structure 471 */ 472 struct pmix_bfrops_base_component_t { 473 pmix_mca_base_component_t base; 474 pmix_mca_base_component_data_t data; 475 int priority; 476 pmix_pointer_array_t types; 477 pmix_bfrop_base_component_assign_module_fn_t assign_module; 478 }; 479 typedef struct pmix_bfrops_base_component_t pmix_bfrops_base_component_t; 480 481 /* 482 * Macro for use in components that are of type bfrops 483 */ 484 #define PMIX_BFROPS_BASE_VERSION_1_0_0 \ 485 PMIX_MCA_BASE_VERSION_1_0_0("bfrops", 1, 0, 0) 486 487 END_C_DECLS 488 489 #endif /* PMIX_BFROP_H */