+/*
+;________________________________________________________________________________
+;
+; Routine: journal_trim_realloc
+;
+; Function: Increase the amount of memory allocated for the list of extents
+; to be unmapped (trimmed). This routine will be called when
+; adding an extent to the list, and the list already occupies
+; all of the space allocated to it. This routine returns ENOMEM
+; if unable to allocate more space, or 0 if the extent list was
+; grown successfully.
+;
+; Input Arguments:
+; tr - The transaction containing the extent list.
+;
+; Output:
+; (result) - ENOMEM or 0.
+;
+; Side effects:
+; The allocated_count and extents fields of tr->trim are updated
+; if the function returned 0.
+;________________________________________________________________________________
+*/
+static int
+journal_trim_realloc(transaction *tr)
+{
+ if (CONFIG_HFS_TRIM) {
+ void *new_extents;
+ uint32_t new_allocated_count;
+
+ new_allocated_count = tr->trim.allocated_count + JOURNAL_DEFAULT_TRIM_EXTENTS;
+ new_extents = kalloc(new_allocated_count * sizeof(dk_extent_t));
+ if (new_extents == NULL) {
+ printf("journal_trim_realloc: unable to grow extent list!\n");
+ /*
+ * Since we could be called when allocating space previously marked
+ * to be trimmed, we need to empty out the list to be safe.
+ */
+ tr->trim.extent_count = 0;
+ return ENOMEM;
+ }
+
+ /* Copy the old extent list to the newly allocated list. */
+ if (tr->trim.extents != NULL) {
+ memmove(new_extents,
+ tr->trim.extents,
+ tr->trim.allocated_count * sizeof(dk_extent_t));
+ kfree(tr->trim.extents,
+ tr->trim.allocated_count * sizeof(dk_extent_t));
+ }
+
+ tr->trim.allocated_count = new_allocated_count;
+ tr->trim.extents = new_extents;
+ }
+ return 0;
+}
+
+
+/*
+;________________________________________________________________________________
+;
+; Routine: journal_trim_add_extent
+;
+; Function: Make note of a range of bytes that should be unmapped
+; (trimmed). That is, the given range of bytes no longer have
+; useful content, and the device can unmap the previous
+; contents. For example, a solid state disk may reuse the
+; underlying storage for other blocks.
+;
+; The extent will be unmapped after the transaction is written
+; to the journal.
+;
+; Input Arguments:
+; jnl - The journal for the volume containing the byte range.
+; offset - The first byte of the range to be trimmed.
+; length - The number of bytes of the extent being trimmed.
+;________________________________________________________________________________
+*/
+__private_extern__ int
+journal_trim_add_extent(journal *jnl, uint64_t offset, uint64_t length)
+{
+ if (CONFIG_HFS_TRIM) {
+ uint64_t end;
+ transaction *tr;
+ dk_extent_t *extent;
+ uint32_t insert_index;
+ uint32_t replace_count;
+
+ CHECK_JOURNAL(jnl);
+
+ if (jnl->flags & JOURNAL_TRIM_ERR) {
+ /*
+ * A previous trim failed, so we have disabled trim for this volume
+ * for as long as it remains mounted.
+ */
+ return 0;
+ }
+
+ if (jnl->flags & JOURNAL_INVALID) {
+ return EINVAL;
+ }
+
+ tr = jnl->active_tr;
+ CHECK_TRANSACTION(tr);
+
+ if (jnl->owner != current_thread()) {
+ panic("jnl: trim_add_extent: called w/out a transaction! jnl %p, owner %p, curact %p\n",
+ jnl, jnl->owner, current_thread());
+ }
+
+ free_old_stuff(jnl);
+
+ end = offset + length;
+
+ /*
+ * Find the range of existing extents that can be combined with the
+ * input extent. We start by counting the number of extents that end
+ * strictly before the input extent, then count the number of extents
+ * that overlap or are contiguous with the input extent.
+ */
+ extent = tr->trim.extents;
+ insert_index = 0;
+ while (insert_index < tr->trim.extent_count && extent->offset + extent->length < offset) {
+ ++insert_index;
+ ++extent;
+ }
+ replace_count = 0;
+ while (insert_index + replace_count < tr->trim.extent_count && extent->offset <= end) {
+ ++replace_count;
+ ++extent;
+ }
+
+ /*
+ * If none of the existing extents can be combined with the input extent,
+ * then just insert it in the list (before item number insert_index).
+ */
+ if (replace_count == 0) {
+ /* If the list was already full, we need to grow it. */
+ if (tr->trim.extent_count == tr->trim.allocated_count) {
+ if (journal_trim_realloc(tr) != 0) {
+ printf("jnl: trim_add_extent: out of memory!");
+ return ENOMEM;
+ }
+ }
+
+ /* Shift any existing extents with larger offsets. */
+ if (insert_index < tr->trim.extent_count) {
+ memmove(&tr->trim.extents[insert_index+1],
+ &tr->trim.extents[insert_index],
+ (tr->trim.extent_count - insert_index) * sizeof(dk_extent_t));
+ }
+ tr->trim.extent_count++;
+
+ /* Store the new extent in the list. */
+ tr->trim.extents[insert_index].offset = offset;
+ tr->trim.extents[insert_index].length = length;
+
+ /* We're done. */
+ return 0;
+ }
+
+ /*
+ * Update extent number insert_index to be the union of the input extent
+ * and all of the replaced extents.
+ */
+ if (tr->trim.extents[insert_index].offset < offset)
+ offset = tr->trim.extents[insert_index].offset;
+ extent = &tr->trim.extents[insert_index + replace_count - 1];
+ if (extent->offset + extent->length > end)
+ end = extent->offset + extent->length;
+ tr->trim.extents[insert_index].offset = offset;
+ tr->trim.extents[insert_index].length = end - offset;
+
+ /*
+ * If we were replacing more than one existing extent, then shift any
+ * extents with larger offsets, and update the count of extents.
+ *
+ * We're going to leave extent #insert_index alone since it was just updated, above.
+ * We need to move extents from index (insert_index + replace_count) through the end of
+ * the list by (replace_count - 1) positions so that they overwrite extent #(insert_index + 1).
+ */
+ if (replace_count > 1 && (insert_index + replace_count) < tr->trim.extent_count) {
+ memmove(&tr->trim.extents[insert_index + 1],
+ &tr->trim.extents[insert_index + replace_count],
+ (tr->trim.extent_count - insert_index - replace_count) * sizeof(dk_extent_t));
+ }
+ tr->trim.extent_count -= replace_count - 1;
+ }
+ return 0;
+}
+
+
+/*
+;________________________________________________________________________________
+;
+; Routine: journal_trim_remove_extent
+;
+; Function: Make note of a range of bytes, some of which may have previously
+; been passed to journal_trim_add_extent, is now in use on the
+; volume. The given bytes will be not be trimmed as part of
+; this transaction.
+;
+; Input Arguments:
+; jnl - The journal for the volume containing the byte range.
+; offset - The first byte of the range to be trimmed.
+; length - The number of bytes of the extent being trimmed.
+;________________________________________________________________________________
+*/
+__private_extern__ int
+journal_trim_remove_extent(journal *jnl, uint64_t offset, uint64_t length)
+{
+ if (CONFIG_HFS_TRIM) {
+ u_int64_t end;
+ dk_extent_t *extent;
+ transaction *tr;
+ u_int32_t keep_before;
+ u_int32_t keep_after;
+
+ CHECK_JOURNAL(jnl);
+
+ if (jnl->flags & JOURNAL_TRIM_ERR) {
+ /*
+ * A previous trim failed, so we have disabled trim for this volume
+ * for as long as it remains mounted.
+ */
+ return 0;
+ }
+
+ if (jnl->flags & JOURNAL_INVALID) {
+ return EINVAL;
+ }
+
+ tr = jnl->active_tr;
+ CHECK_TRANSACTION(tr);
+
+ if (jnl->owner != current_thread()) {
+ panic("jnl: trim_remove_extent: called w/out a transaction! jnl %p, owner %p, curact %p\n",
+ jnl, jnl->owner, current_thread());
+ }
+
+ free_old_stuff(jnl);
+
+ end = offset + length;
+
+ /*
+ * Find any existing extents that start before or end after the input
+ * extent. These extents will be modified if they overlap the input
+ * extent. Other extents between them will be deleted.
+ */
+ extent = tr->trim.extents;
+ keep_before = 0;
+ while (keep_before < tr->trim.extent_count && extent->offset < offset) {
+ ++keep_before;
+ ++extent;
+ }
+ keep_after = keep_before;
+ if (keep_after > 0) {
+ /* See if previous extent extends beyond both ends of input extent. */
+ --keep_after;
+ --extent;
+ }
+ while (keep_after < tr->trim.extent_count && (extent->offset + extent->length) <= end) {
+ ++keep_after;
+ ++extent;
+ }
+
+ /*
+ * When we get here, the first keep_before extents (0 .. keep_before-1)
+ * start before the input extent, and extents (keep_after .. extent_count-1)
+ * end after the input extent. We'll need to keep, all of those extents,
+ * but possibly modify #(keep_before-1) and #keep_after to remove the portion
+ * that overlaps with the input extent.
+ */
+
+ /*
+ * Does the input extent start after and end before the same existing
+ * extent? If so, we have to "punch a hole" in that extent and convert
+ * it to two separate extents.
+ */
+ if (keep_before > keep_after) {
+ /* If the list was already full, we need to grow it. */
+ if (tr->trim.extent_count == tr->trim.allocated_count) {
+ if (journal_trim_realloc(tr) != 0) {
+ printf("jnl: trim_remove_extent: out of memory!");
+ return ENOMEM;
+ }
+ }
+
+ /*
+ * Make room for a new extent by shifting extents #keep_after and later
+ * down by one extent. When we're done, extents #keep_before and
+ * #keep_after will be identical, and we can fall through to removing
+ * the portion that overlaps the input extent.
+ */
+ memmove(&tr->trim.extents[keep_before],
+ &tr->trim.extents[keep_after],
+ (tr->trim.extent_count - keep_after) * sizeof(dk_extent_t));
+ ++tr->trim.extent_count;
+ ++keep_after;
+
+ /*
+ * Fall through. We now have the case where the length of extent
+ * #(keep_before - 1) needs to be updated, and the start of extent
+ * #(keep_after) needs to be updated.
+ */
+ }
+
+ /*
+ * May need to truncate the end of extent #(keep_before - 1) if it overlaps
+ * the input extent.
+ */
+ if (keep_before > 0) {
+ extent = &tr->trim.extents[keep_before - 1];
+ if (extent->offset + extent->length > offset) {
+ extent->length = offset - extent->offset;
+ }
+ }
+
+ /*
+ * May need to update the start of extent #(keep_after) if it overlaps the
+ * input extent.
+ */
+ if (keep_after < tr->trim.extent_count) {
+ extent = &tr->trim.extents[keep_after];
+ if (extent->offset < end) {
+ extent->length = extent->offset + extent->length - end;
+ extent->offset = end;
+ }
+ }
+
+ /*
+ * If there were whole extents that overlapped the input extent, get rid
+ * of them by shifting any following extents, and updating the count.
+ */
+ if (keep_after > keep_before && keep_after < tr->trim.extent_count) {
+ memmove(&tr->trim.extents[keep_before],
+ &tr->trim.extents[keep_after],
+ (tr->trim.extent_count - keep_after) * sizeof(dk_extent_t));
+ }
+ tr->trim.extent_count -= keep_after - keep_before;
+ }
+ return 0;
+}
+
+
+static int
+journal_trim_flush(journal *jnl, transaction *tr)
+{
+ int errno = 0;
+
+ if (CONFIG_HFS_TRIM) {
+ if ((jnl->flags & JOURNAL_TRIM_ERR) == 0 && tr->trim.extent_count > 0) {
+ dk_unmap_t unmap;
+
+ bzero(&unmap, sizeof(unmap));
+ unmap.extents = tr->trim.extents;
+ unmap.extentsCount = tr->trim.extent_count;
+ errno = VNOP_IOCTL(jnl->fsdev, DKIOCUNMAP, (caddr_t)&unmap, FWRITE, vfs_context_kernel());
+ if (errno) {
+ printf("jnl: error %d from DKIOCUNMAP (extents=%lx, count=%u); disabling trim for %s\n",
+ errno, (unsigned long) (tr->trim.extents), tr->trim.extent_count,
+ jnl->jdev_name);
+ jnl->flags |= JOURNAL_TRIM_ERR;
+ }
+ }
+ if (tr->trim.extents) {
+ kfree(tr->trim.extents, tr->trim.allocated_count * sizeof(dk_extent_t));
+ tr->trim.allocated_count = 0;
+ tr->trim.extent_count = 0;
+ tr->trim.extents = NULL;
+ }
+ }
+
+ return errno;
+}
+
+