+#if CONFIG_FREEZE
+/*
+ * We don't have the task lock held while updating the task's
+ * c_seg queues. We can do that because of the following restrictions:
+ *
+ * - SINGLE FREEZER CONTEXT:
+ * We 'insert' c_segs into the task list on the task_freeze path.
+ * There can only be one such freeze in progress and the task
+ * isn't disappearing because we have the VM map lock held throughout
+ * and we have a reference on the proc too.
+ *
+ * - SINGLE TASK DISOWN CONTEXT:
+ * We 'disown' c_segs of a task ONLY from the task_terminate context. So
+ * we don't need the task lock but we need the c_list_lock and the
+ * compressor master lock (shared). We also hold the individual
+ * c_seg locks (exclusive).
+ *
+ * If we either:
+ * - can't get the c_seg lock on a try, then we start again because maybe
+ * the c_seg is part of a compaction and might get freed. So we can't trust
+ * that linkage and need to restart our queue traversal.
+ * - OR, we run into a busy c_seg (say being swapped in or free-ing) we
+ * drop all locks again and wait and restart our queue traversal.
+ *
+ * - The new_owner_task below is currently only the kernel or NULL.
+ *
+ */
+void
+c_seg_update_task_owner(c_segment_t c_seg, task_t new_owner_task)
+{
+ task_t owner_task = c_seg->c_task_owner;
+ uint64_t uncompressed_bytes = ((c_seg->c_slots_used) * PAGE_SIZE_64);
+
+ LCK_MTX_ASSERT(c_list_lock, LCK_MTX_ASSERT_OWNED);
+ LCK_MTX_ASSERT(&c_seg->c_lock, LCK_MTX_ASSERT_OWNED);
+
+ if (owner_task) {
+ task_update_frozen_to_swap_acct(owner_task, uncompressed_bytes, DEBIT_FROM_SWAP);
+ queue_remove(&owner_task->task_frozen_cseg_q, c_seg,
+ c_segment_t, c_task_list_next_cseg);
+ }
+
+ if (new_owner_task) {
+ queue_enter(&new_owner_task->task_frozen_cseg_q, c_seg,
+ c_segment_t, c_task_list_next_cseg);
+ task_update_frozen_to_swap_acct(new_owner_task, uncompressed_bytes, CREDIT_TO_SWAP);
+ }
+
+ c_seg->c_task_owner = new_owner_task;
+}
+
+void
+task_disown_frozen_csegs(task_t owner_task)
+{
+ c_segment_t c_seg = NULL, next_cseg = NULL;
+
+again:
+ PAGE_REPLACEMENT_DISALLOWED(TRUE);
+ lck_mtx_lock_spin_always(c_list_lock);
+
+ for (c_seg = (c_segment_t) queue_first(&owner_task->task_frozen_cseg_q);
+ !queue_end(&owner_task->task_frozen_cseg_q, (queue_entry_t) c_seg);
+ c_seg = next_cseg) {
+ next_cseg = (c_segment_t) queue_next(&c_seg->c_task_list_next_cseg);;
+
+ if (!lck_mtx_try_lock_spin_always(&c_seg->c_lock)) {
+ lck_mtx_unlock(c_list_lock);
+ PAGE_REPLACEMENT_DISALLOWED(FALSE);
+ goto again;
+ }
+
+ if (c_seg->c_busy) {
+ lck_mtx_unlock(c_list_lock);
+ PAGE_REPLACEMENT_DISALLOWED(FALSE);
+
+ c_seg_wait_on_busy(c_seg);
+
+ goto again;
+ }
+ assert(c_seg->c_task_owner == owner_task);
+ c_seg_update_task_owner(c_seg, kernel_task);
+ lck_mtx_unlock_always(&c_seg->c_lock);
+ }
+
+ lck_mtx_unlock(c_list_lock);
+ PAGE_REPLACEMENT_DISALLOWED(FALSE);
+}
+#endif /* CONFIG_FREEZE */