]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
Copy range using fiemap not entire length
authorHaomai Wang <haomaiwang@gmail.com>
Fri, 11 Apr 2014 09:28:13 +0000 (17:28 +0800)
committerJosh Durgin <josh.durgin@inktank.com>
Tue, 29 Apr 2014 00:28:11 +0000 (17:28 -0700)
Under rbd usage, if a volume has tens of thousands of objects and each 4M
object only has several KB(run fio on this volume or other cases), this volume
will be very low performance during a long time after create snapshot on
this volume. The OSD will be busy with large bandwidth read/write although
the object actually has few bytes needed to be copied.

This commit try to use fiemap if backend fs support, it can skip unnecessary
range to write. It also can be beneficial to space effective, because the copied
object will be regard as snapshot object which is access infrequently.

Signed-off-by: Haomai Wang <haomaiwang@gmail.com>
Reviewed-by: Sage Weil <sage@inktank.com>
Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
src/os/FileStore.cc
src/os/FileStore.h

index 9d6252ca4ca9c41a7f593abab2e0ff563a866151..c3ba4db4eddc589a5e89f945c3ec4ab2b8b3caa8 100644 (file)
@@ -2889,7 +2889,7 @@ int FileStore::_clone(coll_t cid, const ghobject_t& oldoid, const ghobject_t& ne
     }
     r = ::ftruncate(**n, 0);
     if (r < 0) {
-      goto out;
+      goto out3;
     }
     struct stat st;
     ::fstat(**o, &st);
@@ -2898,6 +2898,11 @@ int FileStore::_clone(coll_t cid, const ghobject_t& oldoid, const ghobject_t& ne
       r = -errno;
       goto out3;
     }
+    r = ::ftruncate(**n, st.st_size);
+    if (r < 0) {
+      goto out3;
+    }
+
     dout(20) << "objectmap clone" << dendl;
     r = object_map->clone(oldoid, newoid, &spos);
     if (r < 0 && r != -ENOENT)
@@ -2934,6 +2939,125 @@ int FileStore::_do_clone_range(int from, int to, uint64_t srcoff, uint64_t len,
   return backend->clone_range(from, to, srcoff, len, dstoff);
 }
 
+int FileStore::_do_sparse_copy_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff)
+{
+  dout(20) << __func__ << " " << srcoff << "~" << len << " to " << dstoff << dendl;
+  int r = 0;
+  struct fiemap *fiemap = NULL;
+
+  // fiemap doesn't allow zero length
+  if (len == 0)
+    return 0;
+
+  r = backend->do_fiemap(from, srcoff, len, &fiemap);
+  if (r < 0) {
+    derr << "do_fiemap failed:" << srcoff << "~" << len << " = " << r << dendl;
+    return r;
+  }
+
+  // No need to copy
+  if (fiemap->fm_mapped_extents == 0)
+    return r;
+
+  int buflen = 4096*32;
+  char buf[buflen];
+  struct fiemap_extent *extent = &fiemap->fm_extents[0];
+
+  /* start where we were asked to start */
+  if (extent->fe_logical < srcoff) {
+    extent->fe_length -= srcoff - extent->fe_logical;
+    extent->fe_logical = srcoff;
+  }
+
+  uint64_t i = 0;
+
+  while (i < fiemap->fm_mapped_extents) {
+    struct fiemap_extent *next = extent + 1;
+
+    dout(10) << __func__ << " fm_mapped_extents=" << fiemap->fm_mapped_extents
+             << " fe_logical=" << extent->fe_logical << " fe_length="
+             << extent->fe_length << dendl;
+
+    /* try to merge extents */
+    while ((i < fiemap->fm_mapped_extents - 1) &&
+           (extent->fe_logical + extent->fe_length == next->fe_logical)) {
+        next->fe_length += extent->fe_length;
+        next->fe_logical = extent->fe_logical;
+        extent = next;
+        next = extent + 1;
+        i++;
+    }
+
+    if (extent->fe_logical + extent->fe_length > srcoff + len)
+      extent->fe_length = srcoff + len - extent->fe_logical;
+
+    int64_t actual;
+
+    actual = ::lseek64(from, extent->fe_logical, SEEK_SET);
+    if (actual != (int64_t)extent->fe_logical) {
+      r = errno;
+      derr << "lseek64 to " << srcoff << " got " << cpp_strerror(r) << dendl;
+      return r;
+    }
+    actual = ::lseek64(to, extent->fe_logical - srcoff + dstoff, SEEK_SET);
+    if (actual != (int64_t)(extent->fe_logical - srcoff + dstoff)) {
+      r = errno;
+      derr << "lseek64 to " << dstoff << " got " << cpp_strerror(r) << dendl;
+      return r;
+    }
+
+    loff_t pos = 0;
+    loff_t end = extent->fe_length;
+    while (pos < end) {
+      int l = MIN(end-pos, buflen);
+      r = ::read(from, buf, l);
+      dout(25) << "  read from " << pos << "~" << l << " got " << r << dendl;
+      if (r < 0) {
+        if (errno == EINTR) {
+          continue;
+        } else {
+          r = -errno;
+          derr << __func__ << ": read error at " << pos << "~" << len
+              << ", " << cpp_strerror(r) << dendl;
+          break;
+        }
+      }
+      if (r == 0) {
+        r = -ERANGE;
+        derr << __func__ << " got short read result at " << pos
+             << " of fd " << from << " len " << len << dendl;
+        break;
+      }
+      int op = 0;
+      while (op < r) {
+        int r2 = safe_write(to, buf+op, r-op);
+        dout(25) << " write to " << to << " len " << (r-op)
+                 << " got " << r2 << dendl;
+        if (r2 < 0) {
+          r = r2;
+          derr << __func__ << ": write error at " << pos << "~"
+               << r-op << ", " << cpp_strerror(r) << dendl;
+          break;
+        }
+        op += (r-op);
+      }
+      if (r < 0)
+        break;
+      pos += r;
+    }
+    i++;
+    extent++;
+  }
+
+  if (r >= 0 && m_filestore_sloppy_crc) {
+    int rc = backend->_crc_update_clone_range(from, to, srcoff, len, dstoff);
+    assert(rc >= 0);
+  }
+
+  dout(20) << __func__ << " " << srcoff << "~" << len << " to " << dstoff << " = " << r << dendl;
+  return r;
+}
+
 int FileStore::_do_copy_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff)
 {
   dout(20) << "_do_copy_range " << srcoff << "~" << len << " to " << dstoff << dendl;
@@ -2952,7 +3076,7 @@ int FileStore::_do_copy_range(int from, int to, uint64_t srcoff, uint64_t len, u
     derr << "lseek64 to " << dstoff << " got " << cpp_strerror(r) << dendl;
     return r;
   }
-  
+
   loff_t pos = srcoff;
   loff_t end = srcoff + len;
   int buflen = 4096*32;
index 4c9ffdba2f3e87915e1f7b7474cb87b30954d70d..47ca7e4e848a5b8e345ee41bb1ec49e6ded3c3ea 100644 (file)
@@ -461,6 +461,7 @@ public:
                   uint64_t srcoff, uint64_t len, uint64_t dstoff,
                   const SequencerPosition& spos);
   int _do_clone_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff);
+  int _do_sparse_copy_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff);
   int _do_copy_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff);
   int _remove(coll_t cid, const ghobject_t& oid, const SequencerPosition &spos);
 
@@ -670,7 +671,11 @@ protected:
     return filestore->current_fn;
   }
   int _copy_range(int from, int to, uint64_t srcoff, uint64_t len, uint64_t dstoff) {
-    return filestore->_do_copy_range(from, to, srcoff, len, dstoff);
+    if (has_fiemap()) {
+      return filestore->_do_sparse_copy_range(from, to, srcoff, len, dstoff);
+    } else {
+      return filestore->_do_copy_range(from, to, srcoff, len, dstoff);
+    }
   }
   int get_crc_block_size() {
     return filestore->m_filestore_sloppy_crc_block_size;