| @@ -20,8 +20,9 @@ static unsigned __read_mostly afs_cell_min_ttl = 10 * 60; · 19 unmodified lines |
| 20 | 20 | static unsigned __read_mostly afs_cell_max_ttl = 24 * 60 * 60; |
| 21 | 21 | static atomic_t cell_debug_id; |
| 22 | 22 | |
| 23 | | −static void afs_queue_cell_manager(struct afs_net *); |
| 24 | | −static void afs_manage_cell_work(struct work_struct *); |
| 23 | +static void afs_cell_timer(struct timer_list *timer); |
| 24 | +static void afs_destroy_cell_work(struct work_struct *work); |
| 25 | +static void afs_manage_cell_work(struct work_struct *work); |
| 25 | 26 | |
| 26 | 27 | static void afs_dec_cells_outstanding(struct afs_net *net) |
| 27 | 28 | { |
| @@ -29,19 +30,11 @@ static void afs_dec_cells_outstanding(struct afs_net *net) · 1 unmodified line |
| 29 | 30 | wake_up_var(&net->cells_outstanding); |
| 30 | 31 | } |
| 31 | 32 | |
| 32 | | −/* |
| 33 | | − * Set the cell timer to fire after a given delay, assuming it's not already |
| 34 | | − * set for an earlier time. |
| 35 | | − */ |
| 36 | | −static void afs_set_cell_timer(struct afs_net *net, time64_t delay) |
| 33 | +static void afs_set_cell_state(struct afs_cell *cell, enum afs_cell_state state) |
| 37 | 34 | { |
| 38 | | − if (net->live) { |
| 39 | | − atomic_inc(&net->cells_outstanding); |
| 40 | | − if (timer_reduce(&net->cells_timer, jiffies + delay * HZ)) |
| 41 | | − afs_dec_cells_outstanding(net); |
| 42 | | − } else { |
| 43 | | − afs_queue_cell_manager(net); |
| 44 | | − } |
| 35 | + smp_store_release(&cell->state, state); /* Commit cell changes before state */ |
| 36 | + smp_wmb(); /* Set cell state before task state */ |
| 37 | + wake_up_var(&cell->state); |
| 45 | 38 | } |
| 46 | 39 | |
| 47 | 40 | /* |
| @@ -116,7 +109,7 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, · 68 unmodified lines |
| 116 | 109 | const char *name, unsigned int namelen, |
| 117 | 110 | const char *addresses) |
| 118 | 111 | { |
| 119 | | − struct afs_vlserver_list *vllist; |
| 112 | + struct afs_vlserver_list *vllist = NULL; |
| 120 | 113 | struct afs_cell *cell; |
| 121 | 114 | int i, ret; |
| 122 | 115 | |
| @@ -163,7 +156,9 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, · 40 unmodified lines |
| 163 | 156 | cell->net = net; |
| 164 | 157 | refcount_set(&cell->ref, 1); |
| 165 | 158 | atomic_set(&cell->active, 0); |
| 159 | + INIT_WORK(&cell->destroyer, afs_destroy_cell_work); |
| 166 | 160 | INIT_WORK(&cell->manager, afs_manage_cell_work); |
| 161 | + timer_setup(&cell->management_timer, afs_cell_timer, 0); |
| 167 | 162 | init_rwsem(&cell->vs_lock); |
| 168 | 163 | cell->volumes = RB_ROOT; |
| 169 | 164 | INIT_HLIST_HEAD(&cell->proc_volumes); |
| @@ -220,6 +215,7 @@ parse_failed: · 50 unmodified lines |
| 220 | 215 | if (ret == -EINVAL) |
| 221 | 216 | printk(KERN_ERR "kAFS: bad VL server IP address\n"); |
| 222 | 217 | error: |
| 218 | + afs_put_vlserverlist(cell->net, vllist); |
| 223 | 219 | kfree(cell->name - 1); |
| 224 | 220 | kfree(cell); |
| 225 | 221 | _leave(" = %d", ret); |
| @@ -296,26 +292,28 @@ struct afs_cell *afs_lookup_cell(struct afs_net *net, · 70 unmodified lines |
| 296 | 292 | |
| 297 | 293 | cell = candidate; |
| 298 | 294 | candidate = NULL; |
| 299 | | − atomic_set(&cell->active, 2); |
| 300 | | − trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 2, afs_cell_trace_insert); |
| 295 | + afs_use_cell(cell, trace); |
| 301 | 296 | rb_link_node_rcu(&cell->net_node, parent, pp); |
| 302 | 297 | rb_insert_color(&cell->net_node, &net->cells); |
| 303 | 298 | up_write(&net->cells_lock); |
| 304 | 299 | |
| 305 | | − afs_queue_cell(cell, afs_cell_trace_get_queue_new); |
| 300 | + afs_queue_cell(cell, afs_cell_trace_queue_new); |
| 306 | 301 | |
| 307 | 302 | wait_for_cell: |
| 308 | | − trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), atomic_read(&cell->active), |
| 309 | | − afs_cell_trace_wait); |
| 310 | 303 | _debug("wait_for_cell"); |
| 311 | | − wait_var_event(&cell->state, |
| 312 | | − ({ |
| 313 | | − state = smp_load_acquire(&cell->state); /* vs error */ |
| 314 | | − state == AFS_CELL_ACTIVE || state == AFS_CELL_REMOVED; |
| 315 | | − })); |
| 304 | + state = smp_load_acquire(&cell->state); /* vs error */ |
| 305 | + if (state != AFS_CELL_ACTIVE && |
| 306 | + state != AFS_CELL_DEAD) { |
| 307 | + afs_see_cell(cell, afs_cell_trace_wait); |
| 308 | + wait_var_event(&cell->state, |
| 309 | + ({ |
| 310 | + state = smp_load_acquire(&cell->state); /* vs error */ |
| 311 | + state == AFS_CELL_ACTIVE || state == AFS_CELL_DEAD; |
| 312 | + })); |
| 313 | + } |
| 316 | 314 | |
| 317 | 315 | /* Check the state obtained from the wait check. */ |
| 318 | | − if (state == AFS_CELL_REMOVED) { |
| 316 | + if (state == AFS_CELL_DEAD) { |
| 319 | 317 | ret = cell->error; |
| 320 | 318 | goto error; |
| 321 | 319 | } |
| @@ -397,7 +395,6 @@ int afs_cell_init(struct afs_net *net, const char *rootcell) · 75 unmodified lines |
| 397 | 395 | |
| 398 | 396 | /* install the new cell */ |
| 399 | 397 | down_write(&net->cells_lock); |
| 400 | | − afs_see_cell(new_root, afs_cell_trace_see_ws); |
| 401 | 398 | old_root = rcu_replace_pointer(net->ws_cell, new_root, |
| 402 | 399 | lockdep_is_held(&net->cells_lock)); |
| 403 | 400 | up_write(&net->cells_lock); |
| @@ -530,30 +527,14 @@ static void afs_cell_destroy(struct rcu_head *rcu) · 126 unmodified lines |
| 530 | 527 | _leave(" [destroyed]"); |
| 531 | 528 | } |
| 532 | 529 | |
| 533 | | −/* |
| 534 | | − * Queue the cell manager. |
| 535 | | − */ |
| 536 | | −static void afs_queue_cell_manager(struct afs_net *net) |
| 537 | | −{ |
| 538 | | − int outstanding = atomic_inc_return(&net->cells_outstanding); |
| 539 | | − |
| 540 | | − _enter("%d", outstanding); |
| 541 | | − |
| 542 | | − if (!queue_work(afs_wq, &net->cells_manager)) |
| 543 | | − afs_dec_cells_outstanding(net); |
| 544 | | −} |
| 545 | | − |
| 546 | | −/* |
| 547 | | − * Cell management timer. We have an increment on cells_outstanding that we |
| 548 | | − * need to pass along to the work item. |
| 549 | | − */ |
| 550 | | −void afs_cells_timer(struct timer_list *timer) |
| 530 | +static void afs_destroy_cell_work(struct work_struct *work) |
| 551 | 531 | { |
| 552 | | − struct afs_net *net = container_of(timer, struct afs_net, cells_timer); |
| 532 | + struct afs_cell *cell = container_of(work, struct afs_cell, destroyer); |
| 553 | 533 | |
| 554 | | − _enter(""); |
| 555 | | − if (!queue_work(afs_wq, &net->cells_manager)) |
| 556 | | − afs_dec_cells_outstanding(net); |
| 534 | + afs_see_cell(cell, afs_cell_trace_destroy); |
| 535 | + timer_delete_sync(&cell->management_timer); |
| 536 | + cancel_work_sync(&cell->manager); |
| 537 | + call_rcu(&cell->rcu, afs_cell_destroy); |
| 557 | 538 | } |
| 558 | 539 | |
| 559 | 540 | /* |
| @@ -585,7 +566,7 @@ void afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason) · 25 unmodified lines |
| 585 | 566 | if (zero) { |
| 586 | 567 | a = atomic_read(&cell->active); |
| 587 | 568 | WARN(a != 0, "Cell active count %u > 0\n", a); |
| 588 | | − call_rcu(&cell->rcu, afs_cell_destroy); |
| 569 | + WARN_ON(!queue_work(afs_wq, &cell->destroyer)); |
| 589 | 570 | } |
| 590 | 571 | } |
| 591 | 572 | } |
| @@ -597,10 +578,9 @@ struct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason) · 5 unmodified lines |
| 597 | 578 | { |
| 598 | 579 | int r, a; |
| 599 | 580 | |
| 600 | | − r = refcount_read(&cell->ref); |
| 601 | | − WARN_ON(r == 0); |
| 581 | + __refcount_inc(&cell->ref, &r); |
| 602 | 582 | a = atomic_inc_return(&cell->active); |
| 603 | | − trace_afs_cell(cell->debug_id, r, a, reason); |
| 583 | + trace_afs_cell(cell->debug_id, r + 1, a, reason); |
| 604 | 584 | return cell; |
| 605 | 585 | } |
| 606 | 586 | |
| @@ -612,6 +592,7 @@ void afs_unuse_cell(struct afs_cell *cell, enum afs_cell_trace reason) · 5 unmodified lines |
| 612 | 592 | { |
| 613 | 593 | unsigned int debug_id; |
| 614 | 594 | time64_t now, expire_delay; |
| 595 | + bool zero; |
| 615 | 596 | int r, a; |
| 616 | 597 | |
| 617 | 598 | if (!cell) |
| @@ -626,13 +607,15 @@ void afs_unuse_cell(struct afs_cell *cell, enum afs_cell_trace reason) · 8 unmodified lines |
| 626 | 607 | expire_delay = afs_cell_gc_delay; |
| 627 | 608 | |
| 628 | 609 | debug_id = cell->debug_id; |
| 629 | | − r = refcount_read(&cell->ref); |
| 630 | 610 | a = atomic_dec_return(&cell->active); |
| 631 | | − trace_afs_cell(debug_id, r, a, reason); |
| 632 | | − WARN_ON(a == 0); |
| 633 | | − if (a == 1) |
| 611 | + if (!a) |
| 634 | 612 | /* 'cell' may now be garbage collected. */ |
| 635 | | − afs_set_cell_timer(cell->net, expire_delay); |
| 613 | + afs_set_cell_timer(cell, expire_delay); |
| 614 | + |
| 615 | + zero = __refcount_dec_and_test(&cell->ref, &r); |
| 616 | + trace_afs_cell(debug_id, r - 1, a, reason); |
| 617 | + if (zero) |
| 618 | + WARN_ON(!queue_work(afs_wq, &cell->destroyer)); |
| 636 | 619 | } |
| 637 | 620 | |
| 638 | 621 | /* |
| @@ -652,9 +635,27 @@ void afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason) · 13 unmodified lines |
| 652 | 635 | */ |
| 653 | 636 | void afs_queue_cell(struct afs_cell *cell, enum afs_cell_trace reason) |
| 654 | 637 | { |
| 655 | | − afs_get_cell(cell, reason); |
| 656 | | − if (!queue_work(afs_wq, &cell->manager)) |
| 657 | | − afs_put_cell(cell, afs_cell_trace_put_queue_fail); |
| 638 | + queue_work(afs_wq, &cell->manager); |
| 639 | +} |
| 640 | + |
| 641 | +/* |
| 642 | + * Cell-specific management timer. |
| 643 | + */ |
| 644 | +static void afs_cell_timer(struct timer_list *timer) |
| 645 | +{ |
| 646 | + struct afs_cell *cell = container_of(timer, struct afs_cell, management_timer); |
| 647 | + |
| 648 | + afs_see_cell(cell, afs_cell_trace_see_mgmt_timer); |
| 649 | + if (refcount_read(&cell->ref) > 0 && cell->net->live) |
| 650 | + queue_work(afs_wq, &cell->manager); |
| 651 | +} |
| 652 | + |
| 653 | +/* |
| 654 | + * Set/reduce the cell timer. |
| 655 | + */ |
| 656 | +void afs_set_cell_timer(struct afs_cell *cell, unsigned int delay_secs) |
| 657 | +{ |
| 658 | + timer_reduce(&cell->management_timer, jiffies + delay_secs * HZ); |
| 658 | 659 | } |
| 659 | 660 | |
| 660 | 661 | /* |
| @@ -737,212 +738,125 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) · 76 unmodified lines |
| 737 | 738 | _leave(""); |
| 738 | 739 | } |
| 739 | 740 | |
| 741 | +static bool afs_has_cell_expired(struct afs_cell *cell, time64_t *_next_manage) |
| 742 | +{ |
| 743 | + const struct afs_vlserver_list *vllist; |
| 744 | + time64_t expire_at = cell->last_inactive; |
| 745 | + time64_t now = ktime_get_real_seconds(); |
| 746 | + |
| 747 | + if (atomic_read(&cell->active)) |
| 748 | + return false; |
| 749 | + if (!cell->net->live) |
| 750 | + return true; |
| 751 | + |
| 752 | + vllist = rcu_dereference_protected(cell->vl_servers, true); |
| 753 | + if (vllist && vllist->nr_servers > 0) |
| 754 | + expire_at += afs_cell_gc_delay; |
| 755 | + |
| 756 | + if (expire_at <= now) |
| 757 | + return true; |
| 758 | + if (expire_at < *_next_manage) |
| 759 | + *_next_manage = expire_at; |
| 760 | + return false; |
| 761 | +} |
| 762 | + |
| 740 | 763 | /* |
| 741 | 764 | * Manage a cell record, initialising and destroying it, maintaining its DNS |
| 742 | 765 | * records. |
| 743 | 766 | */ |
| 744 | | −static void afs_manage_cell(struct afs_cell *cell) |
| 767 | +static bool afs_manage_cell(struct afs_cell *cell) |
| 745 | 768 | { |
| 746 | 769 | struct afs_net *net = cell->net; |
| 747 | | − int ret, active; |
| 770 | + time64_t next_manage = TIME64_MAX; |
| 771 | + int ret; |
| 748 | 772 | |
| 749 | 773 | _enter("%s", cell->name); |
| 750 | 774 | |
| 751 | | −again: |
| 752 | 775 | _debug("state %u", cell->state); |
| 753 | 776 | switch (cell->state) { |
| 754 | | − case AFS_CELL_INACTIVE: |
| 755 | | − case AFS_CELL_FAILED: |
| 756 | | − down_write(&net->cells_lock); |
| 757 | | − active = 1; |
| 758 | | − if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) { |
| 759 | | − rb_erase(&cell->net_node, &net->cells); |
| 760 | | − trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), 0, |
| 761 | | − afs_cell_trace_unuse_delete); |
| 762 | | − smp_store_release(&cell->state, AFS_CELL_REMOVED); |
| 763 | | − } |
| 764 | | − up_write(&net->cells_lock); |
| 765 | | − if (cell->state == AFS_CELL_REMOVED) { |
| 766 | | − wake_up_var(&cell->state); |
| 767 | | − goto final_destruction; |
| 768 | | − } |
| 769 | | − if (cell->state == AFS_CELL_FAILED) |
| 770 | | − goto done; |
| 771 | | − smp_store_release(&cell->state, AFS_CELL_UNSET); |
| 772 | | − wake_up_var(&cell->state); |
| 773 | | − goto again; |
| 774 | | − |
| 775 | | − case AFS_CELL_UNSET: |
| 776 | | − smp_store_release(&cell->state, AFS_CELL_ACTIVATING); |
| 777 | | − wake_up_var(&cell->state); |
| 778 | | − goto again; |
| 779 | | − |
| 780 | | − case AFS_CELL_ACTIVATING: |
| 781 | | − ret = afs_activate_cell(net, cell); |
| 782 | | − if (ret < 0) |
| 783 | | − goto activation_failed; |
| 777 | + case AFS_CELL_SETTING_UP: |
| 778 | + goto set_up_cell; |
| 779 | + case AFS_CELL_ACTIVE: |
| 780 | + goto cell_is_active; |
| 781 | + case AFS_CELL_REMOVING: |
| 782 | + WARN_ON_ONCE(1); |
| 783 | + return false; |
| 784 | + case AFS_CELL_DEAD: |
| 785 | + return false; |
| 786 | + default: |
| 787 | + _debug("bad state %u", cell->state); |
| 788 | + WARN_ON_ONCE(1); /* Unhandled state */ |
| 789 | + return false; |
| 790 | + } |
| 784 | 791 | |
| 785 | | − smp_store_release(&cell->state, AFS_CELL_ACTIVE); |
| 786 | | − wake_up_var(&cell->state); |
| 787 | | − goto again; |
| 792 | +set_up_cell: |
| 793 | + ret = afs_activate_cell(net, cell); |
| 794 | + if (ret < 0) { |
| 795 | + cell->error = ret; |
| 796 | + goto remove_cell; |
| 797 | + } |
| 788 | 798 | |
| 789 | | − case AFS_CELL_ACTIVE: |
| 790 | | − if (atomic_read(&cell->active) > 1) { |
| 791 | | − if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) { |
| 792 | | − ret = afs_update_cell(cell); |
| 793 | | − if (ret < 0) |
| 794 | | − cell->error = ret; |
| 795 | | − } |
| 796 | | − goto done; |
| 797 | | − } |
| 798 | | − smp_store_release(&cell->state, AFS_CELL_DEACTIVATING); |
| 799 | | − wake_up_var(&cell->state); |
| 800 | | − goto again; |
| 799 | + afs_set_cell_state(cell, AFS_CELL_ACTIVE); |
| 801 | 800 | |
| 802 | | − case AFS_CELL_DEACTIVATING: |
| 803 | | − if (atomic_read(&cell->active) > 1) |
| 804 | | − goto reverse_deactivation; |
| 805 | | − afs_deactivate_cell(net, cell); |
| 806 | | − smp_store_release(&cell->state, AFS_CELL_INACTIVE); |
| 807 | | − wake_up_var(&cell->state); |
| 808 | | − goto again; |
| 801 | +cell_is_active: |
| 802 | + if (afs_has_cell_expired(cell, &next_manage)) |
| 803 | + goto remove_cell; |
| 809 | 804 | |
| 810 | | − case AFS_CELL_REMOVED: |
| 811 | | − goto done; |
| 805 | + if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) { |
| 806 | + ret = afs_update_cell(cell); |
| 807 | + if (ret < 0) |
| 808 | + cell->error = ret; |
| 809 | + } |
| 812 | 810 | |
| 813 | | − default: |
| 814 | | − break; |
| 811 | + if (next_manage < TIME64_MAX && cell->net->live) { |
| 812 | + time64_t now = ktime_get_real_seconds(); |
| 813 | + |
| 814 | + if (next_manage - now <= 0) |
| 815 | + afs_queue_cell(cell, afs_cell_trace_queue_again); |
| 816 | + else |
| 817 | + afs_set_cell_timer(cell, next_manage - now); |
| 815 | 818 | } |
| 816 | | − _debug("bad state %u", cell->state); |
| 817 | | − BUG(); /* Unhandled state */ |
| 819 | + _leave(" [done %u]", cell->state); |
| 820 | + return false; |
| 818 | 821 | |
| 819 | | −activation_failed: |
| 820 | | − cell->error = ret; |
| 821 | | − afs_deactivate_cell(net, cell); |
| 822 | +remove_cell: |
| 823 | + down_write(&net->cells_lock); |
| 822 | 824 | |
| 823 | | − smp_store_release(&cell->state, AFS_CELL_FAILED); /* vs error */ |
| 824 | | − wake_up_var(&cell->state); |
| 825 | | − goto again; |
| 825 | + if (atomic_read(&cell->active)) { |
| 826 | + up_write(&net->cells_lock); |
| 827 | + goto cell_is_active; |
| 828 | + } |
| 826 | 829 | |
| 827 | | −reverse_deactivation: |
| 828 | | − smp_store_release(&cell->state, AFS_CELL_ACTIVE); |
| 829 | | − wake_up_var(&cell->state); |
| 830 | | − _leave(" [deact->act]"); |
| 831 | | − return; |
| 830 | + /* Make sure that the expiring server records are going to see the fact |
| 831 | + * that the cell is caput. |
| 832 | + */ |
| 833 | + afs_set_cell_state(cell, AFS_CELL_REMOVING); |
| 832 | 834 | |
| 833 | | −done: |
| 834 | | − _leave(" [done %u]", cell->state); |
| 835 | | − return; |
| 835 | + afs_deactivate_cell(net, cell); |
| 836 | + afs_purge_servers(cell); |
| 837 | + |
| 838 | + rb_erase(&cell->net_node, &net->cells); |
| 839 | + afs_see_cell(cell, afs_cell_trace_unuse_delete); |
| 840 | + up_write(&net->cells_lock); |
| 836 | 841 | |
| 837 | | −final_destruction: |
| 838 | 842 | /* The root volume is pinning the cell */ |
| 839 | 843 | afs_put_volume(cell->root_volume, afs_volume_trace_put_cell_root); |
| 840 | 844 | cell->root_volume = NULL; |
| 841 | | − afs_purge_servers(cell); |
| 842 | | − afs_put_cell(cell, afs_cell_trace_put_destroy); |
| 845 | + |
| 846 | + afs_set_cell_state(cell, AFS_CELL_DEAD); |
| 847 | + return true; |
| 843 | 848 | } |
| 844 | 849 | |
| 845 | 850 | static void afs_manage_cell_work(struct work_struct *work) |
| 846 | 851 | { |
| 847 | 852 | struct afs_cell *cell = container_of(work, struct afs_cell, manager); |
| 853 | + bool final_put; |
| 848 | 854 | |
| 849 | | − afs_manage_cell(cell); |
| 850 | | − afs_put_cell(cell, afs_cell_trace_put_queue_work); |
| 851 | | −} |
| 852 | | − |
| 853 | | −/* |
| 854 | | − * Manage the records of cells known to a network namespace. This includes |
| 855 | | − * updating the DNS records and garbage collecting unused cells that were |
| 856 | | − * automatically added. |
| 857 | | − * |
| 858 | | − * Note that constructed cell records may only be removed from net->cells by |
| 859 | | − * this work item, so it is safe for this work item to stash a cursor pointing |
| 860 | | − * into the tree and then return to caller (provided it skips cells that are |
| 861 | | − * still under construction). |
| 862 | | − * |
| 863 | | − * Note also that we were given an increment on net->cells_outstanding by |
| 864 | | − * whoever queued us that we need to deal with before returning. |
| 865 | | − */ |
| 866 | | −void afs_manage_cells(struct work_struct *work) |
| 867 | | −{ |
| 868 | | − struct afs_net *net = container_of(work, struct afs_net, cells_manager); |
| 869 | | − struct rb_node *cursor; |
| 870 | | − time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX; |
| 871 | | − bool purging = !net->live; |
| 872 | | − |
| 873 | | − _enter(""); |
| 874 | | − |
| 875 | | − /* Trawl the cell database looking for cells that have expired from |
| 876 | | − * lack of use and cells whose DNS results have expired and dispatch |
| 877 | | − * their managers. |
| 878 | | − */ |
| 879 | | − down_read(&net->cells_lock); |
| 880 | | − |
| 881 | | − for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) { |
| 882 | | − struct afs_cell *cell = |
| 883 | | − rb_entry(cursor, struct afs_cell, net_node); |
| 884 | | − unsigned active; |
| 885 | | − bool sched_cell = false; |
| 886 | | − |
| 887 | | − active = atomic_read(&cell->active); |
| 888 | | − trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), |
| 889 | | − active, afs_cell_trace_manage); |
| 890 | | − |
| 891 | | − ASSERTCMP(active, >=, 1); |
| 892 | | − |
| 893 | | − if (purging) { |
| 894 | | − if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) { |
| 895 | | − active = atomic_dec_return(&cell->active); |
| 896 | | − trace_afs_cell(cell->debug_id, refcount_read(&cell->ref), |
| 897 | | − active, afs_cell_trace_unuse_pin); |
| 898 | | − } |
| 899 | | − } |
| 900 | | − |
| 901 | | − if (active == 1) { |
| 902 | | − struct afs_vlserver_list *vllist; |
| 903 | | − time64_t expire_at = cell->last_inactive; |
| 904 | | − |
| 905 | | − read_lock(&cell->vl_servers_lock); |
| 906 | | − vllist = rcu_dereference_protected( |
| 907 | | − cell->vl_servers, |
| 908 | | − lockdep_is_held(&cell->vl_servers_lock)); |
| 909 | | − if (vllist->nr_servers > 0) |
| 910 | | − expire_at += afs_cell_gc_delay; |
| 911 | | − read_unlock(&cell->vl_servers_lock); |
| 912 | | − if (purging || expire_at <= now) |
| 913 | | − sched_cell = true; |
| 914 | | − else if (expire_at < next_manage) |
| 915 | | − next_manage = expire_at; |
| 916 | | − } |
| 917 | | − |
| 918 | | − if (!purging) { |
| 919 | | − if (test_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) |
| 920 | | − sched_cell = true; |
| 921 | | − } |
| 922 | | − |
| 923 | | − if (sched_cell) |
| 924 | | − afs_queue_cell(cell, afs_cell_trace_get_queue_manage); |
| 925 | | − } |
| 926 | | − |
| 927 | | − up_read(&net->cells_lock); |
| 928 | | − |
| 929 | | − /* Update the timer on the way out. We have to pass an increment on |
| 930 | | − * cells_outstanding in the namespace that we are in to the timer or |
| 931 | | − * the work scheduler. |
| 932 | | − */ |
| 933 | | − if (!purging && next_manage < TIME64_MAX) { |
| 934 | | − now = ktime_get_real_seconds(); |
| 935 | | − |
| 936 | | − if (next_manage - now <= 0) { |
| 937 | | − if (queue_work(afs_wq, &net->cells_manager)) |
| 938 | | − atomic_inc(&net->cells_outstanding); |
| 939 | | − } else { |
| 940 | | − afs_set_cell_timer(net, next_manage - now); |
| 941 | | − } |
| 942 | | − } |
| 943 | | − |
| 944 | | − afs_dec_cells_outstanding(net); |
| 945 | | − _leave(" [%d]", atomic_read(&net->cells_outstanding)); |
| 855 | + afs_see_cell(cell, afs_cell_trace_manage); |
| 856 | + final_put = afs_manage_cell(cell); |
| 857 | + afs_see_cell(cell, afs_cell_trace_managed); |
| 858 | + if (final_put) |
| 859 | + afs_put_cell(cell, afs_cell_trace_put_final); |
| 946 | 860 | } |
| 947 | 861 | |
| 948 | 862 | /* |
| @@ -951,6 +865,7 @@ void afs_manage_cells(struct work_struct *work) · 2 unmodified lines |
| 951 | 865 | void afs_cell_purge(struct afs_net *net) |
| 952 | 866 | { |
| 953 | 867 | struct afs_cell *ws; |
| 868 | + struct rb_node *cursor; |
| 954 | 869 | |
| 955 | 870 | _enter(""); |
| 956 | 871 | |
| @@ -960,12 +875,19 @@ void afs_cell_purge(struct afs_net *net) · 3 unmodified lines |
| 960 | 875 | up_write(&net->cells_lock); |
| 961 | 876 | afs_unuse_cell(ws, afs_cell_trace_unuse_ws); |
| 962 | 877 | |
| 963 | | − _debug("del timer"); |
| 964 | | − if (del_timer_sync(&net->cells_timer)) |
| 965 | | − atomic_dec(&net->cells_outstanding); |
| 878 | + _debug("kick cells"); |
| 879 | + down_read(&net->cells_lock); |
| 880 | + for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) { |
| 881 | + struct afs_cell *cell = rb_entry(cursor, struct afs_cell, net_node); |
| 882 | + |
| 883 | + afs_see_cell(cell, afs_cell_trace_purge); |
| 966 | 884 | |
| 967 | | − _debug("kick mgr"); |
| 968 | | − afs_queue_cell_manager(net); |
| 885 | + if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) |
| 886 | + afs_unuse_cell(cell, afs_cell_trace_unuse_pin); |
| 887 | + |
| 888 | + afs_queue_cell(cell, afs_cell_trace_queue_purge); |
| 889 | + } |
| 890 | + up_read(&net->cells_lock); |
| 969 | 891 | |
| 970 | 892 | _debug("wait"); |
| 971 | 893 | wait_var_event(&net->cells_outstanding, |