1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2023 Red Hat 4 */ 5 6 #include "index-session.h" 7 8 #include <linux/atomic.h> 9 10 #include "logger.h" 11 #include "memory-alloc.h" 12 #include "time-utils.h" 13 14 #include "funnel-requestqueue.h" 15 #include "index.h" 16 #include "index-layout.h" 17 18 /* 19 * The index session contains a lock (the request_mutex) which ensures that only one thread can 20 * change the state of its index at a time. The state field indicates the current state of the 21 * index through a set of descriptive flags. The request_mutex must be notified whenever a 22 * non-transient state flag is cleared. The request_mutex is also used to count the number of 23 * requests currently in progress so that they can be drained when suspending or closing the index. 24 * 25 * If the index session is suspended shortly after opening an index, it may have to suspend during 26 * a rebuild. Depending on the size of the index, a rebuild may take a significant amount of time, 27 * so UDS allows the rebuild to be paused in order to suspend the session in a timely manner. When 28 * the index session is resumed, the rebuild can continue from where it left off. If the index 29 * session is shut down with a suspended rebuild, the rebuild progress is abandoned and the rebuild 30 * will start from the beginning the next time the index is loaded. The mutex and status fields in 31 * the index_load_context are used to record the state of any interrupted rebuild. 32 */ 33 34 enum index_session_flag_bit { 35 IS_FLAG_BIT_START = 8, 36 /* The session has started loading an index but not completed it. */ 37 IS_FLAG_BIT_LOADING = IS_FLAG_BIT_START, 38 /* The session has loaded an index, which can handle requests. */ 39 IS_FLAG_BIT_LOADED, 40 /* The session's index has been permanently disabled. */ 41 IS_FLAG_BIT_DISABLED, 42 /* The session's index is suspended. */ 43 IS_FLAG_BIT_SUSPENDED, 44 /* The session is handling some index state change. */ 45 IS_FLAG_BIT_WAITING, 46 /* The session's index is closing and draining requests. */ 47 IS_FLAG_BIT_CLOSING, 48 /* The session is being destroyed and is draining requests. */ 49 IS_FLAG_BIT_DESTROYING, 50 }; 51 52 enum index_session_flag { 53 IS_FLAG_LOADED = (1 << IS_FLAG_BIT_LOADED), 54 IS_FLAG_LOADING = (1 << IS_FLAG_BIT_LOADING), 55 IS_FLAG_DISABLED = (1 << IS_FLAG_BIT_DISABLED), 56 IS_FLAG_SUSPENDED = (1 << IS_FLAG_BIT_SUSPENDED), 57 IS_FLAG_WAITING = (1 << IS_FLAG_BIT_WAITING), 58 IS_FLAG_CLOSING = (1 << IS_FLAG_BIT_CLOSING), 59 IS_FLAG_DESTROYING = (1 << IS_FLAG_BIT_DESTROYING), 60 }; 61 62 /* Release a reference to an index session. */ 63 static void release_index_session(struct uds_index_session *index_session) 64 { 65 mutex_lock(&index_session->request_mutex); 66 if (--index_session->request_count == 0) 67 uds_broadcast_cond(&index_session->request_cond); 68 mutex_unlock(&index_session->request_mutex); 69 } 70 71 /* 72 * Acquire a reference to the index session for an asynchronous index request. The reference must 73 * eventually be released with a corresponding call to release_index_session(). 74 */ 75 static int get_index_session(struct uds_index_session *index_session) 76 { 77 unsigned int state; 78 int result = UDS_SUCCESS; 79 80 mutex_lock(&index_session->request_mutex); 81 index_session->request_count++; 82 state = index_session->state; 83 mutex_unlock(&index_session->request_mutex); 84 85 if (state == IS_FLAG_LOADED) { 86 return UDS_SUCCESS; 87 } else if (state & IS_FLAG_DISABLED) { 88 result = UDS_DISABLED; 89 } else if ((state & IS_FLAG_LOADING) || 90 (state & IS_FLAG_SUSPENDED) || 91 (state & IS_FLAG_WAITING)) { 92 result = -EBUSY; 93 } else { 94 result = UDS_NO_INDEX; 95 } 96 97 release_index_session(index_session); 98 return result; 99 } 100 101 int uds_launch_request(struct uds_request *request) 102 { 103 int result; 104 105 if (request->callback == NULL) { 106 vdo_log_error("missing required callback"); 107 return -EINVAL; 108 } 109 110 switch (request->type) { 111 case UDS_DELETE: 112 case UDS_POST: 113 case UDS_QUERY: 114 case UDS_QUERY_NO_UPDATE: 115 case UDS_UPDATE: 116 break; 117 default: 118 vdo_log_error("received invalid callback type"); 119 return -EINVAL; 120 } 121 122 /* Reset all internal fields before processing. */ 123 memset(&request->internal, 0, sizeof(request->internal)); 124 125 result = get_index_session(request->session); 126 if (result != UDS_SUCCESS) 127 return result; 128 129 request->found = false; 130 request->unbatched = false; 131 request->index = request->session->index; 132 133 uds_enqueue_request(request, STAGE_TRIAGE); 134 return UDS_SUCCESS; 135 } 136 137 static void enter_callback_stage(struct uds_request *request) 138 { 139 if (request->status != UDS_SUCCESS) { 140 /* All request errors are considered unrecoverable */ 141 mutex_lock(&request->session->request_mutex); 142 request->session->state |= IS_FLAG_DISABLED; 143 mutex_unlock(&request->session->request_mutex); 144 } 145 146 uds_request_queue_enqueue(request->session->callback_queue, request); 147 } 148 149 static inline void count_once(u64 *count_ptr) 150 { 151 WRITE_ONCE(*count_ptr, READ_ONCE(*count_ptr) + 1); 152 } 153 154 static void update_session_stats(struct uds_request *request) 155 { 156 struct session_stats *session_stats = &request->session->stats; 157 158 count_once(&session_stats->requests); 159 160 switch (request->type) { 161 case UDS_POST: 162 if (request->found) 163 count_once(&session_stats->posts_found); 164 else 165 count_once(&session_stats->posts_not_found); 166 167 if (request->location == UDS_LOCATION_IN_OPEN_CHAPTER) 168 count_once(&session_stats->posts_found_open_chapter); 169 else if (request->location == UDS_LOCATION_IN_DENSE) 170 count_once(&session_stats->posts_found_dense); 171 else if (request->location == UDS_LOCATION_IN_SPARSE) 172 count_once(&session_stats->posts_found_sparse); 173 break; 174 175 case UDS_UPDATE: 176 if (request->found) 177 count_once(&session_stats->updates_found); 178 else 179 count_once(&session_stats->updates_not_found); 180 break; 181 182 case UDS_DELETE: 183 if (request->found) 184 count_once(&session_stats->deletions_found); 185 else 186 count_once(&session_stats->deletions_not_found); 187 break; 188 189 case UDS_QUERY: 190 case UDS_QUERY_NO_UPDATE: 191 if (request->found) 192 count_once(&session_stats->queries_found); 193 else 194 count_once(&session_stats->queries_not_found); 195 break; 196 197 default: 198 request->status = VDO_ASSERT(false, "unknown request type: %d", 199 request->type); 200 } 201 } 202 203 static void handle_callbacks(struct uds_request *request) 204 { 205 struct uds_index_session *index_session = request->session; 206 207 if (request->status == UDS_SUCCESS) 208 update_session_stats(request); 209 210 request->status = uds_status_to_errno(request->status); 211 request->callback(request); 212 release_index_session(index_session); 213 } 214 215 static int __must_check make_empty_index_session(struct uds_index_session **index_session_ptr) 216 { 217 int result; 218 struct uds_index_session *session; 219 220 result = vdo_allocate(1, struct uds_index_session, __func__, &session); 221 if (result != VDO_SUCCESS) 222 return result; 223 224 mutex_init(&session->request_mutex); 225 uds_init_cond(&session->request_cond); 226 mutex_init(&session->load_context.mutex); 227 uds_init_cond(&session->load_context.cond); 228 229 result = uds_make_request_queue("callbackW", &handle_callbacks, 230 &session->callback_queue); 231 if (result != UDS_SUCCESS) { 232 vdo_free(session); 233 return result; 234 } 235 236 *index_session_ptr = session; 237 return UDS_SUCCESS; 238 } 239 240 int uds_create_index_session(struct uds_index_session **session) 241 { 242 if (session == NULL) { 243 vdo_log_error("missing session pointer"); 244 return -EINVAL; 245 } 246 247 return uds_status_to_errno(make_empty_index_session(session)); 248 } 249 250 static int __must_check start_loading_index_session(struct uds_index_session *index_session) 251 { 252 int result; 253 254 mutex_lock(&index_session->request_mutex); 255 if (index_session->state & IS_FLAG_SUSPENDED) { 256 vdo_log_info("Index session is suspended"); 257 result = -EBUSY; 258 } else if (index_session->state != 0) { 259 vdo_log_info("Index is already loaded"); 260 result = -EBUSY; 261 } else { 262 index_session->state |= IS_FLAG_LOADING; 263 result = UDS_SUCCESS; 264 } 265 mutex_unlock(&index_session->request_mutex); 266 return result; 267 } 268 269 static void finish_loading_index_session(struct uds_index_session *index_session, 270 int result) 271 { 272 mutex_lock(&index_session->request_mutex); 273 index_session->state &= ~IS_FLAG_LOADING; 274 if (result == UDS_SUCCESS) 275 index_session->state |= IS_FLAG_LOADED; 276 277 uds_broadcast_cond(&index_session->request_cond); 278 mutex_unlock(&index_session->request_mutex); 279 } 280 281 static int initialize_index_session(struct uds_index_session *index_session, 282 enum uds_open_index_type open_type) 283 { 284 int result; 285 struct uds_configuration *config; 286 287 result = uds_make_configuration(&index_session->parameters, &config); 288 if (result != UDS_SUCCESS) { 289 vdo_log_error_strerror(result, "Failed to allocate config"); 290 return result; 291 } 292 293 memset(&index_session->stats, 0, sizeof(index_session->stats)); 294 result = uds_make_index(config, open_type, &index_session->load_context, 295 enter_callback_stage, &index_session->index); 296 if (result != UDS_SUCCESS) 297 vdo_log_error_strerror(result, "Failed to make index"); 298 else 299 uds_log_configuration(config); 300 301 uds_free_configuration(config); 302 return result; 303 } 304 305 static const char *get_open_type_string(enum uds_open_index_type open_type) 306 { 307 switch (open_type) { 308 case UDS_CREATE: 309 return "creating index"; 310 case UDS_LOAD: 311 return "loading or rebuilding index"; 312 case UDS_NO_REBUILD: 313 return "loading index"; 314 default: 315 return "unknown open method"; 316 } 317 } 318 319 /* 320 * Open an index under the given session. This operation will fail if the 321 * index session is suspended, or if there is already an open index. 322 */ 323 int uds_open_index(enum uds_open_index_type open_type, 324 const struct uds_parameters *parameters, 325 struct uds_index_session *session) 326 { 327 int result; 328 char name[BDEVNAME_SIZE]; 329 330 if (parameters == NULL) { 331 vdo_log_error("missing required parameters"); 332 return -EINVAL; 333 } 334 if (parameters->bdev == NULL) { 335 vdo_log_error("missing required block device"); 336 return -EINVAL; 337 } 338 if (session == NULL) { 339 vdo_log_error("missing required session pointer"); 340 return -EINVAL; 341 } 342 343 result = start_loading_index_session(session); 344 if (result != UDS_SUCCESS) 345 return uds_status_to_errno(result); 346 347 session->parameters = *parameters; 348 format_dev_t(name, parameters->bdev->bd_dev); 349 vdo_log_info("%s: %s", get_open_type_string(open_type), name); 350 351 result = initialize_index_session(session, open_type); 352 if (result != UDS_SUCCESS) 353 vdo_log_error_strerror(result, "Failed %s", 354 get_open_type_string(open_type)); 355 356 finish_loading_index_session(session, result); 357 return uds_status_to_errno(result); 358 } 359 360 static void wait_for_no_requests_in_progress(struct uds_index_session *index_session) 361 { 362 mutex_lock(&index_session->request_mutex); 363 while (index_session->request_count > 0) { 364 uds_wait_cond(&index_session->request_cond, 365 &index_session->request_mutex); 366 } 367 mutex_unlock(&index_session->request_mutex); 368 } 369 370 static int __must_check save_index(struct uds_index_session *index_session) 371 { 372 wait_for_no_requests_in_progress(index_session); 373 return uds_save_index(index_session->index); 374 } 375 376 static void suspend_rebuild(struct uds_index_session *session) 377 { 378 mutex_lock(&session->load_context.mutex); 379 switch (session->load_context.status) { 380 case INDEX_OPENING: 381 session->load_context.status = INDEX_SUSPENDING; 382 383 /* Wait until the index indicates that it is not replaying. */ 384 while ((session->load_context.status != INDEX_SUSPENDED) && 385 (session->load_context.status != INDEX_READY)) { 386 uds_wait_cond(&session->load_context.cond, 387 &session->load_context.mutex); 388 } 389 390 break; 391 392 case INDEX_READY: 393 /* Index load does not need to be suspended. */ 394 break; 395 396 case INDEX_SUSPENDED: 397 case INDEX_SUSPENDING: 398 case INDEX_FREEING: 399 default: 400 /* These cases should not happen. */ 401 VDO_ASSERT_LOG_ONLY(false, "Bad load context state %u", 402 session->load_context.status); 403 break; 404 } 405 mutex_unlock(&session->load_context.mutex); 406 } 407 408 /* 409 * Suspend index operation, draining all current index requests and preventing new index requests 410 * from starting. Optionally saves all index data before returning. 411 */ 412 int uds_suspend_index_session(struct uds_index_session *session, bool save) 413 { 414 int result = UDS_SUCCESS; 415 bool no_work = false; 416 bool rebuilding = false; 417 418 /* Wait for any current index state change to complete. */ 419 mutex_lock(&session->request_mutex); 420 while (session->state & IS_FLAG_CLOSING) 421 uds_wait_cond(&session->request_cond, &session->request_mutex); 422 423 if ((session->state & IS_FLAG_WAITING) || (session->state & IS_FLAG_DESTROYING)) { 424 no_work = true; 425 vdo_log_info("Index session is already changing state"); 426 result = -EBUSY; 427 } else if (session->state & IS_FLAG_SUSPENDED) { 428 no_work = true; 429 } else if (session->state & IS_FLAG_LOADING) { 430 session->state |= IS_FLAG_WAITING; 431 rebuilding = true; 432 } else if (session->state & IS_FLAG_LOADED) { 433 session->state |= IS_FLAG_WAITING; 434 } else { 435 no_work = true; 436 session->state |= IS_FLAG_SUSPENDED; 437 uds_broadcast_cond(&session->request_cond); 438 } 439 mutex_unlock(&session->request_mutex); 440 441 if (no_work) 442 return uds_status_to_errno(result); 443 444 if (rebuilding) 445 suspend_rebuild(session); 446 else if (save) 447 result = save_index(session); 448 else 449 result = uds_flush_index_session(session); 450 451 mutex_lock(&session->request_mutex); 452 session->state &= ~IS_FLAG_WAITING; 453 session->state |= IS_FLAG_SUSPENDED; 454 uds_broadcast_cond(&session->request_cond); 455 mutex_unlock(&session->request_mutex); 456 return uds_status_to_errno(result); 457 } 458 459 static int replace_device(struct uds_index_session *session, struct block_device *bdev) 460 { 461 int result; 462 463 result = uds_replace_index_storage(session->index, bdev); 464 if (result != UDS_SUCCESS) 465 return result; 466 467 session->parameters.bdev = bdev; 468 return UDS_SUCCESS; 469 } 470 471 /* 472 * Resume index operation after being suspended. If the index is suspended and the supplied block 473 * device differs from the current backing store, the index will start using the new backing store. 474 */ 475 int uds_resume_index_session(struct uds_index_session *session, 476 struct block_device *bdev) 477 { 478 int result = UDS_SUCCESS; 479 bool no_work = false; 480 bool resume_replay = false; 481 482 mutex_lock(&session->request_mutex); 483 if (session->state & IS_FLAG_WAITING) { 484 vdo_log_info("Index session is already changing state"); 485 no_work = true; 486 result = -EBUSY; 487 } else if (!(session->state & IS_FLAG_SUSPENDED)) { 488 /* If not suspended, just succeed. */ 489 no_work = true; 490 result = UDS_SUCCESS; 491 } else { 492 session->state |= IS_FLAG_WAITING; 493 if (session->state & IS_FLAG_LOADING) 494 resume_replay = true; 495 } 496 mutex_unlock(&session->request_mutex); 497 498 if (no_work) 499 return result; 500 501 if ((session->index != NULL) && (bdev != session->parameters.bdev)) { 502 result = replace_device(session, bdev); 503 if (result != UDS_SUCCESS) { 504 mutex_lock(&session->request_mutex); 505 session->state &= ~IS_FLAG_WAITING; 506 uds_broadcast_cond(&session->request_cond); 507 mutex_unlock(&session->request_mutex); 508 return uds_status_to_errno(result); 509 } 510 } 511 512 if (resume_replay) { 513 mutex_lock(&session->load_context.mutex); 514 switch (session->load_context.status) { 515 case INDEX_SUSPENDED: 516 session->load_context.status = INDEX_OPENING; 517 /* Notify the index to start replaying again. */ 518 uds_broadcast_cond(&session->load_context.cond); 519 break; 520 521 case INDEX_READY: 522 /* There is no index rebuild to resume. */ 523 break; 524 525 case INDEX_OPENING: 526 case INDEX_SUSPENDING: 527 case INDEX_FREEING: 528 default: 529 /* These cases should not happen; do nothing. */ 530 VDO_ASSERT_LOG_ONLY(false, "Bad load context state %u", 531 session->load_context.status); 532 break; 533 } 534 mutex_unlock(&session->load_context.mutex); 535 } 536 537 mutex_lock(&session->request_mutex); 538 session->state &= ~IS_FLAG_WAITING; 539 session->state &= ~IS_FLAG_SUSPENDED; 540 uds_broadcast_cond(&session->request_cond); 541 mutex_unlock(&session->request_mutex); 542 return UDS_SUCCESS; 543 } 544 545 static int save_and_free_index(struct uds_index_session *index_session) 546 { 547 int result = UDS_SUCCESS; 548 bool suspended; 549 struct uds_index *index = index_session->index; 550 551 if (index == NULL) 552 return UDS_SUCCESS; 553 554 mutex_lock(&index_session->request_mutex); 555 suspended = (index_session->state & IS_FLAG_SUSPENDED); 556 mutex_unlock(&index_session->request_mutex); 557 558 if (!suspended) { 559 result = uds_save_index(index); 560 if (result != UDS_SUCCESS) 561 vdo_log_warning_strerror(result, 562 "ignoring error from save_index"); 563 } 564 uds_free_index(index); 565 index_session->index = NULL; 566 567 /* 568 * Reset all index state that happens to be in the index 569 * session, so it doesn't affect any future index. 570 */ 571 mutex_lock(&index_session->load_context.mutex); 572 index_session->load_context.status = INDEX_OPENING; 573 mutex_unlock(&index_session->load_context.mutex); 574 575 mutex_lock(&index_session->request_mutex); 576 /* Only the suspend bit will remain relevant. */ 577 index_session->state &= IS_FLAG_SUSPENDED; 578 mutex_unlock(&index_session->request_mutex); 579 580 return result; 581 } 582 583 /* Save and close the current index. */ 584 int uds_close_index(struct uds_index_session *index_session) 585 { 586 int result = UDS_SUCCESS; 587 588 /* Wait for any current index state change to complete. */ 589 mutex_lock(&index_session->request_mutex); 590 while ((index_session->state & IS_FLAG_WAITING) || 591 (index_session->state & IS_FLAG_CLOSING)) { 592 uds_wait_cond(&index_session->request_cond, 593 &index_session->request_mutex); 594 } 595 596 if (index_session->state & IS_FLAG_SUSPENDED) { 597 vdo_log_info("Index session is suspended"); 598 result = -EBUSY; 599 } else if ((index_session->state & IS_FLAG_DESTROYING) || 600 !(index_session->state & IS_FLAG_LOADED)) { 601 /* The index doesn't exist, hasn't finished loading, or is being destroyed. */ 602 result = UDS_NO_INDEX; 603 } else { 604 index_session->state |= IS_FLAG_CLOSING; 605 } 606 mutex_unlock(&index_session->request_mutex); 607 if (result != UDS_SUCCESS) 608 return uds_status_to_errno(result); 609 610 vdo_log_debug("Closing index"); 611 wait_for_no_requests_in_progress(index_session); 612 result = save_and_free_index(index_session); 613 vdo_log_debug("Closed index"); 614 615 mutex_lock(&index_session->request_mutex); 616 index_session->state &= ~IS_FLAG_CLOSING; 617 uds_broadcast_cond(&index_session->request_cond); 618 mutex_unlock(&index_session->request_mutex); 619 return uds_status_to_errno(result); 620 } 621 622 /* This will save and close an open index before destroying the session. */ 623 int uds_destroy_index_session(struct uds_index_session *index_session) 624 { 625 int result; 626 bool load_pending = false; 627 628 vdo_log_debug("Destroying index session"); 629 630 /* Wait for any current index state change to complete. */ 631 mutex_lock(&index_session->request_mutex); 632 while ((index_session->state & IS_FLAG_WAITING) || 633 (index_session->state & IS_FLAG_CLOSING)) { 634 uds_wait_cond(&index_session->request_cond, 635 &index_session->request_mutex); 636 } 637 638 if (index_session->state & IS_FLAG_DESTROYING) { 639 mutex_unlock(&index_session->request_mutex); 640 vdo_log_info("Index session is already closing"); 641 return -EBUSY; 642 } 643 644 index_session->state |= IS_FLAG_DESTROYING; 645 load_pending = ((index_session->state & IS_FLAG_LOADING) && 646 (index_session->state & IS_FLAG_SUSPENDED)); 647 mutex_unlock(&index_session->request_mutex); 648 649 if (load_pending) { 650 /* Tell the index to terminate the rebuild. */ 651 mutex_lock(&index_session->load_context.mutex); 652 if (index_session->load_context.status == INDEX_SUSPENDED) { 653 index_session->load_context.status = INDEX_FREEING; 654 uds_broadcast_cond(&index_session->load_context.cond); 655 } 656 mutex_unlock(&index_session->load_context.mutex); 657 658 /* Wait until the load exits before proceeding. */ 659 mutex_lock(&index_session->request_mutex); 660 while (index_session->state & IS_FLAG_LOADING) { 661 uds_wait_cond(&index_session->request_cond, 662 &index_session->request_mutex); 663 } 664 mutex_unlock(&index_session->request_mutex); 665 } 666 667 wait_for_no_requests_in_progress(index_session); 668 result = save_and_free_index(index_session); 669 uds_request_queue_finish(index_session->callback_queue); 670 index_session->callback_queue = NULL; 671 vdo_log_debug("Destroyed index session"); 672 vdo_free(index_session); 673 return uds_status_to_errno(result); 674 } 675 676 /* Wait until all callbacks for index operations are complete. */ 677 int uds_flush_index_session(struct uds_index_session *index_session) 678 { 679 wait_for_no_requests_in_progress(index_session); 680 uds_wait_for_idle_index(index_session->index); 681 return UDS_SUCCESS; 682 } 683 684 /* Statistics collection is intended to be thread-safe. */ 685 static void collect_stats(const struct uds_index_session *index_session, 686 struct uds_index_stats *stats) 687 { 688 const struct session_stats *session_stats = &index_session->stats; 689 690 stats->current_time = ktime_to_seconds(current_time_ns(CLOCK_REALTIME)); 691 stats->posts_found = READ_ONCE(session_stats->posts_found); 692 stats->in_memory_posts_found = READ_ONCE(session_stats->posts_found_open_chapter); 693 stats->dense_posts_found = READ_ONCE(session_stats->posts_found_dense); 694 stats->sparse_posts_found = READ_ONCE(session_stats->posts_found_sparse); 695 stats->posts_not_found = READ_ONCE(session_stats->posts_not_found); 696 stats->updates_found = READ_ONCE(session_stats->updates_found); 697 stats->updates_not_found = READ_ONCE(session_stats->updates_not_found); 698 stats->deletions_found = READ_ONCE(session_stats->deletions_found); 699 stats->deletions_not_found = READ_ONCE(session_stats->deletions_not_found); 700 stats->queries_found = READ_ONCE(session_stats->queries_found); 701 stats->queries_not_found = READ_ONCE(session_stats->queries_not_found); 702 stats->requests = READ_ONCE(session_stats->requests); 703 } 704 705 int uds_get_index_session_stats(struct uds_index_session *index_session, 706 struct uds_index_stats *stats) 707 { 708 if (stats == NULL) { 709 vdo_log_error("received a NULL index stats pointer"); 710 return -EINVAL; 711 } 712 713 collect_stats(index_session, stats); 714 if (index_session->index != NULL) { 715 uds_get_index_stats(index_session->index, stats); 716 } else { 717 stats->entries_indexed = 0; 718 stats->memory_used = 0; 719 stats->collisions = 0; 720 stats->entries_discarded = 0; 721 } 722 723 return UDS_SUCCESS; 724 } 725 726 void uds_wait_cond(struct cond_var *cv, struct mutex *mutex) 727 { 728 DEFINE_WAIT(__wait); 729 730 prepare_to_wait(&cv->wait_queue, &__wait, TASK_IDLE); 731 mutex_unlock(mutex); 732 schedule(); 733 finish_wait(&cv->wait_queue, &__wait); 734 mutex_lock(mutex); 735 } 736