Просмотр исходного кода

dpkg: Try to preallocate the disk size for extracted files

This might help in avoiding filesystem fragmentation, and possibly
improve performance on some filesystems.

We use the system specific methods if available, and only use
posix_fallocate() if nothing else is available, because on some systems
its semantics are counter to what we want to obtain here, as the libc
library will fallback to manually writing '\0' to each block to force
the preallocation, instead of just failing and leaving the application
to do so if desired.
Guillem Jover лет назад: 12
Родитель
Сommit
87b0b20b86
6 измененных файлов с 91 добавлено и 1 удалено
  1. 4 1
      configure.ac
  2. 3 0
      debian/changelog
  3. 75 0
      lib/dpkg/fdio.c
  4. 3 0
      lib/dpkg/fdio.h
  5. 1 0
      lib/dpkg/libdpkg.map
  6. 5 0
      src/archives.c

+ 4 - 1
configure.ac

@@ -99,6 +99,8 @@ DPKG_CHECK_DECL([makedev], [sys/types.h])
 DPKG_CHECK_DECL([WCOREDUMP], [sys/wait.h])
 DPKG_CHECK_DECL([TIOCNOTTY], [sys/ioctl.h])
 DPKG_CHECK_DECL([O_NOFOLLOW], [fcntl.h])
+DPKG_CHECK_DECL([F_ALLOCSP64], [fcntl.h])
+DPKG_CHECK_DECL([F_PREALLOCATE], [fcntl.h])
 DPKG_CHECK_DECL([P_tmpdir], [stdio.h])
 DPKG_CHECK_PROGNAME
 DPKG_CHECK_COMPAT_FUNCS([getopt getopt_long obstack_free \
@@ -108,7 +110,8 @@ AC_CHECK_DECLS([strnlen], [[#include <string.h>]])
 AC_CHECK_FUNCS([memcpy lchown],
                [], [AC_MSG_ERROR([missing required function])])
 AC_CHECK_FUNCS([strtoimax isascii setsid getdtablesize \
-                getprogname getexecname lutimes posix_fadvise])
+                getprogname getexecname lutimes \
+                fallocate posix_fallocate posix_fadvise])
 
 DPKG_MMAP
 

+ 3 - 0
debian/changelog

@@ -59,6 +59,9 @@ dpkg (1.17.11) UNRELEASED; urgency=low
   * Clarify error message in Dpkg::Source::Quilt when patches fail to apply,
     to note that the patch might be malformed (besides not accepting patches
     with fuzz).
+  * Try to preallocate the disk size for extracted files on unpack. This
+    might help in avoiding filesystem fragmentation, and possibly improve
+    performance on some filesystems.
 
   [ Updated programs translations ]
   * Danish (Joe Dalton). Closes: #754127

+ 75 - 0
lib/dpkg/fdio.c

@@ -22,6 +22,7 @@
 #include <compat.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 
 #include <dpkg/fdio.h>
@@ -75,3 +76,77 @@ fd_write(int fd, const void *buf, size_t len)
 
 	return total;
 }
+
+#ifdef HAVE_F_PREALLOCATE
+static void
+fd_preallocate_setup(fstore_t *fs, int flags, off_t offset, off_t len)
+{
+	fs->fst_flags = flags;
+	fs->fst_posmode = F_PEOFPOSMODE;
+	fs->fst_offset = offset;
+	fs->fst_length = len;
+	fs->fst_bytesalloc = 0;
+}
+#endif
+
+/**
+ * Request the kernel to allocate the specified size for a file descriptor.
+ *
+ * We only want to send a hint that we will be using the requested size. But
+ * we do not want to unnecessarily write the file contents. That is why we
+ * are not using posix_fallocate(3) directly if possible, and not at all
+ * on glibc based systems (except on GNU/kFreeBSD).
+ */
+int
+fd_allocate_size(int fd, off_t offset, off_t len)
+{
+	int rc;
+
+	if (len == 0)
+		return 0;
+
+#if defined(HAVE_F_PREALLOCATE)
+	/* On Mac OS X. */
+	fstore_t fs;
+
+	fs_preallocate_setup(&fs, F_ALLOCATECONTIG, offset, len);
+	rc = fcntl(fd, F_PREALLOCATE, &fs)
+	if (rc < 0 && errno == ENOSPC) {
+		/* If we cannot get a contiguous allocation, then try
+		 * non-contiguous. */
+		fs_preallocate_setup(&fs, F_ALLOCATEALL, offset, len);
+		rc = fcntl(fd, F_PREALLOCATE, &fs)
+	}
+#elif defined(HAVE_F_ALLOCSP64)
+	/* On Solaris. */
+	struct flock64 fl;
+
+	fl.l_whence = SEEK_SET;
+	fl.l_start = offset;
+	fl.l_len = len;
+
+	rc = fcntl(fd, F_ALLOCSP64, &fl);
+#elif defined(HAVE_FALLOCATE)
+	/* On Linux. */
+	do {
+		rc = fallocate(fd, 0, offset, len);
+	} while (rc < 0 && errno == EINTR);
+#elif defined(HAVE_POSIX_FALLOCATE) && \
+      ((defined(__GLIBC__) && defined(__FreeBSD_kernel__)) || \
+       !defined(__GLIBC__))
+	/*
+	 * On BSDs, newer GNU/kFreeBSD and other non-glibc based systems
+	 * we can use posix_fallocate(2) which should be a simple syscall
+	 * wrapper. But not on other glibc systems, as there the function
+	 * will try to allocate the size by writing a '\0' to each block
+	 * if the syscall is not implemented or not supported by the
+	 * kernel or the filesystem, which we do not want.
+	 */
+	rc = posix_fallocate(fd, offset, len);
+#else
+	errno = ENOSYS;
+	rc = -1;
+#endif
+
+	return rc;
+}

+ 3 - 0
lib/dpkg/fdio.h

@@ -36,6 +36,9 @@ DPKG_BEGIN_DECLS
 ssize_t fd_read(int fd, void *buf, size_t len);
 ssize_t fd_write(int fd, const void *buf, size_t len);
 
+int
+fd_allocate_size(int fd, off_t offset, off_t len);
+
 /** @} */
 
 DPKG_END_DECLS

+ 1 - 0
lib/dpkg/libdpkg.map

@@ -125,6 +125,7 @@ LIBDPKG_PRIVATE {
 	# Buffer I/O functions
 	fd_read;
 	fd_write;
+	fd_allocate_size;
 	buffer_filter;
 	buffer_skip_*;
 	buffer_copy_*;

+ 5 - 0
src/archives.c

@@ -337,6 +337,11 @@ tarobject_extract(struct tarcontext *tc, struct tar_entry *te,
     debug(dbg_eachfiledetail, "tarobject file open size=%jd",
           (intmax_t)te->size);
 
+    /* We try to tell the filesystem how much disk space we are going to
+     * need to let it reduce fragmentation and possibly improve performance,
+     * as we do know the size beforehand. */
+    fd_allocate_size(fd, 0, te->size);
+
     newhash = nfmalloc(MD5HASHLEN + 1);
     if (fd_fd_copy_and_md5(tc->backendpipe, fd, newhash, te->size, &err) < 0)
       ohshit(_("cannot copy extracted data for '%.255s' to '%.255s': %s"),