+/* Return number of blocks that aren't mapped in the [start, start + size]
+ * region */
+static int osd_count_not_mapped(struct osd_object *obj, uint64_t start,
+ uint32_t size)
+{
+ dmu_buf_impl_t *dbi = (dmu_buf_impl_t *)obj->oo_db;
+ dmu_buf_impl_t *db;
+ dnode_t *dn;
+ uint32_t blkshift;
+ uint64_t end, blkid;
+ int rc;
+ ENTRY;
+
+ DB_DNODE_ENTER(dbi);
+ dn = DB_DNODE(dbi);
+
+ if (dn->dn_maxblkid == 0) {
+ if (start + size <= dn->dn_datablksz)
+ GOTO(out, size = 0);
+ if (start < dn->dn_datablksz)
+ start = dn->dn_datablksz;
+ /* assume largest block size */
+ blkshift = osd_spa_maxblockshift(
+ dmu_objset_spa(osd_obj2dev(obj)->od_os));
+ } else {
+ /* blocksize can't change */
+ blkshift = dn->dn_datablkshift;
+ }
+
+ /* compute address of last block */
+ end = (start + size - 1) >> blkshift;
+ /* align start on block boundaries */
+ start >>= blkshift;
+
+ /* size is null, can't be mapped */
+ if (obj->oo_attr.la_size == 0 || dn->dn_maxblkid == 0)
+ GOTO(out, size = (end - start + 1) << blkshift);
+
+ /* beyond EOF, can't be mapped */
+ if (start > dn->dn_maxblkid)
+ GOTO(out, size = (end - start + 1) << blkshift);
+
+ size = 0;
+ for (blkid = start; blkid <= end; blkid++) {
+ if (blkid == dn->dn_maxblkid)
+ /* this one is mapped for sure */
+ continue;
+ if (blkid > dn->dn_maxblkid) {
+ size += (end - blkid + 1) << blkshift;
+ GOTO(out, size);
+ }
+
+ rc = dbuf_hold_impl(dn, 0, blkid, TRUE, FTAG, &db);
+ if (rc) {
+ /* for ENOENT (block not mapped) and any other errors,
+ * assume the block isn't mapped */
+ size += 1 << blkshift;
+ continue;
+ }
+ dbuf_rele(db, FTAG);
+ }
+
+ GOTO(out, size);
+out:
+ DB_DNODE_EXIT(dbi);
+ return size;
+}
+