+#if DEVELOPMENT || DEBUG
+
+void
+vm_compressor_inject_error(int *slot)
+{
+ c_slot_mapping_t slot_ptr = (c_slot_mapping_t)slot;
+
+ /* No error detection for single-value compression. */
+ if (slot_ptr->s_cseg == C_SV_CSEG_ID) {
+ printf("%s(): cannot inject errors in SV-compressed pages\n", __func__ );
+ return;
+ }
+
+ /* s_cseg is actually "segno+1" */
+ const uint32_t c_segno = slot_ptr->s_cseg - 1;
+
+ assert(c_segno < c_segments_available);
+ assert(c_segments[c_segno].c_segno >= c_segments_available);
+
+ const c_segment_t c_seg = c_segments[c_segno].c_seg;
+
+ PAGE_REPLACEMENT_DISALLOWED(TRUE);
+
+ lck_mtx_lock_spin_always(&c_seg->c_lock);
+ assert(c_seg->c_state != C_IS_EMPTY && c_seg->c_state != C_IS_FREE);
+
+ const uint16_t c_indx = slot_ptr->s_cindx;
+ assert(c_indx < c_seg->c_nextslot);
+
+ /*
+ * To safely make this segment temporarily writable, we need to mark
+ * the segment busy, which allows us to release the segment lock.
+ */
+ while (c_seg->c_busy) {
+ c_seg_wait_on_busy(c_seg);
+ lck_mtx_lock_spin_always(&c_seg->c_lock);
+ }
+ C_SEG_BUSY(c_seg);
+
+ bool already_writable = (c_seg->c_state == C_IS_FILLING);
+ if (!already_writable) {
+ /*
+ * Protection update must be performed preemptibly, so temporarily drop
+ * the lock. Having set c_busy will prevent most other concurrent
+ * operations.
+ */
+ lck_mtx_unlock_always(&c_seg->c_lock);
+ C_SEG_MAKE_WRITEABLE(c_seg);
+ lck_mtx_lock_spin_always(&c_seg->c_lock);
+ }
+
+ /*
+ * Once we've released the lock following our c_state == C_IS_FILLING check,
+ * c_current_seg_filled() can (re-)write-protect the segment. However, it
+ * will transition from C_IS_FILLING before releasing the c_seg lock, so we
+ * can detect this by re-checking after we've reobtained the lock.
+ */
+ if (already_writable && c_seg->c_state != C_IS_FILLING) {
+ lck_mtx_unlock_always(&c_seg->c_lock);
+ C_SEG_MAKE_WRITEABLE(c_seg);
+ lck_mtx_lock_spin_always(&c_seg->c_lock);
+ already_writable = false;
+ /* Segment can't be freed while c_busy is set. */
+ assert(c_seg->c_state != C_IS_FILLING);
+ }
+
+ c_slot_t cs = C_SEG_SLOT_FROM_INDEX(c_seg, c_indx);
+ int32_t *data = &c_seg->c_store.c_buffer[cs->c_offset];
+ /* assume that the compressed data holds at least one int32_t */
+ assert(UNPACK_C_SIZE(cs) > sizeof(*data));
+ /*
+ * This bit is known to be in the payload of a MISS packet resulting from
+ * the pattern used in the test pattern from decompression_failure.c.
+ * Flipping it should result in many corrupted bits in the test page.
+ */
+ data[0] ^= 0x00000100;
+ if (!already_writable) {
+ lck_mtx_unlock_always(&c_seg->c_lock);
+ C_SEG_WRITE_PROTECT(c_seg);
+ lck_mtx_lock_spin_always(&c_seg->c_lock);
+ }
+
+ C_SEG_WAKEUP_DONE(c_seg);
+ lck_mtx_unlock_always(&c_seg->c_lock);
+
+ PAGE_REPLACEMENT_DISALLOWED(FALSE);
+}
+
+#endif /* DEVELOPMENT || DEBUG */