these functions help with allocating buffers, aligning,
reading and writing files opened with O_DIRECT.

Signed-off-by: Claudio Fontana <cfont...@suse.de>
---
 src/libvirt_private.syms |   6 ++
 src/util/virfile.c       | 174 +++++++++++++++++++++++++++++++++++++++
 src/util/virfile.h       |   9 ++
 3 files changed, 189 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index bfedd85326..702f640b5d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2240,7 +2240,13 @@ virFileComparePaths;
 virFileCopyACLs;
 virFileDataSync;
 virFileDeleteTree;
+virFileDirectAlign;
+virFileDirectBufferNew;
+virFileDirectCopyBuf;
 virFileDirectFdFlag;
+virFileDirectRead;
+virFileDirectReadCopy;
+virFileDirectWrite;
 virFileExists;
 virFileFclose;
 virFileFdopen;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index 99da058db3..e8500704e5 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -192,6 +192,135 @@ virFileDirectFdFlag(void)
     return O_DIRECT ? O_DIRECT : -1;
 }
 
+/**
+ * virFileDirectAlign: align a value, which may include a pointer.
+ *
+ * @value: the value that will be incremented to reach alignment
+ *
+ * Returns the aligned value.
+ */
+uintptr_t
+virFileDirectAlign(uintptr_t value)
+{
+    return (value + VIR_FILE_DIRECT_ALIGN_MASK) & ~VIR_FILE_DIRECT_ALIGN_MASK;
+}
+
+/**
+ * virFileDirectRead: perform a single aligned read.
+ * @fd:    O_DIRECT file descriptor
+ * @buf:   aligned buffer to read into
+ * @count: the desired read size. Note that if unaligned,
+ *         extra bytes will be read based on alignment.
+ *
+ * Note that buf should be able to contain count plus the alignment!
+ *
+ * Returns < 0 and errno set on error, or the number of bytes read,
+ * which may be smaller or even greater than count.
+ */
+ssize_t
+virFileDirectRead(int fd, void *buf, size_t count)
+{
+    size_t aligned_count = virFileDirectAlign(count);
+    while (count > 0) {
+        ssize_t r = read(fd, buf, aligned_count);
+        if (r < 0 && errno == EINTR)
+            continue;
+        return r;
+    }
+    return 0;
+}
+
+/**
+ * virFileDirectCopyBuf: copy a buffer contents to destination in memory
+ * @buf:     aligned buffer to copy from
+ * @count:   amount of data to copy
+ * @dst:     the destination in memory
+ * @dst_len: the maximum the destination can hold.
+ *
+ * copies up to count bytes from buf into dst, but not more than dst_len.
+ * increments buf pointer and dst pointer, as well as decrementing the
+ * maximum the destination can hold (dst_len).
+ *
+ * Returns the amount copied.
+ */
+size_t
+virFileDirectCopyBuf(void **buf, size_t count, void **dst, size_t *dst_len)
+{
+    size_t to_copy;
+
+    to_copy = count > *dst_len ? *dst_len : count;
+    memcpy(*dst, *buf, to_copy);
+    *dst_len -= to_copy;
+    *(char **)dst += to_copy;
+    *(char **)buf += to_copy;
+    return to_copy;
+}
+
+/**
+ * virFileDirectReadCopy: read an fd and copy contents to memory
+ * @fd:      O_DIRECT file descriptor
+ * @buf:     aligned buffer to read the fd into, and then copy from
+ * @buflen:  size of the buffer
+ * @dst:     the destination in memory
+ * @dst_len: the maximum the destination can hold.
+ *
+ * reads data from the fd file descriptor into the buffer,
+ * and then copy to the destination, filling it up to dst_len.
+ *
+ * Returns < 0 and errno set on error,
+ * or the number of bytes read, which may be past the requested dst_len,
+ * or may be smaller if the fd does not contain enough data.
+ *
+ * The buf pointer is updated to point to eventual exccess data in the buffer.
+ */
+ssize_t
+virFileDirectReadCopy(int fd, void **buf, size_t buflen, void *dst, size_t 
dst_len)
+{
+    ssize_t nread = 0;
+    void *d = dst;
+    char *s = *buf;
+
+    while (dst_len > 0) {
+        ssize_t rv;
+        *buf = s;
+        rv = virFileDirectRead(fd, s, dst_len < buflen ? dst_len : buflen);
+        if (rv < 0)
+            return rv;
+        if (rv == 0)
+            return nread; /* not enough data to fulfill request */
+
+        nread += rv;      /* note, we might read past the requested len */
+        virFileDirectCopyBuf(buf, rv, &d, &dst_len);
+    }
+    return nread;
+}
+
+/**
+ * virFileDirectWrite: perform a single aligned write.
+ * @fd:    O_DIRECT file descriptor to write to
+ * @buf:   aligned buffer to write from
+ * @count: the desired write size. Note that if unaligned,
+ *         extra 0 bytes will be written based on alignment.
+ *
+ * Returns < 0 and errno set on error, or the number of bytes written,
+ * which may be smaller or even greater than count.
+ */
+ssize_t
+virFileDirectWrite(int fd, void *buf, size_t count)
+{
+    size_t aligned_count = virFileDirectAlign(count);
+    if (aligned_count > count) {
+        memset((char *)buf + count, 0, aligned_count - count);
+    }
+    while (count > 0) {
+        ssize_t r = write(fd, buf, aligned_count); /* sc_avoid_write */
+        if (r < 0 && errno == EINTR)
+            continue;
+        return r;
+    }
+    return 0;
+}
+
 /* Opaque type for managing a wrapper around a fd.  For now,
  * read-write is not supported, just a single direction.  */
 struct _virFileWrapperFd {
@@ -202,6 +331,41 @@ struct _virFileWrapperFd {
 
 #ifndef WIN32
 
+/**
+ * virFileDirectBufferNew: allocate a buffer and return the first
+ *                         block-aligned address in it.
+ *
+ * @alloc_base: pointer to the to-be-allocated memory buffer.
+ * @buflen: desired length, which should be greater than alignment.
+ *
+ * Allocate a memory area large enough to accommodate an aligned
+ * buffer of size buflen.
+ *
+ * On success, *alloc_base is set to the newly allocated memory,
+ * and the aligned buffer within it is returned.
+ *
+ * On failure, *alloc_base is set to NULL and the function
+ * returns NULL.
+ */
+void *
+virFileDirectBufferNew(void **alloc_base, size_t buflen)
+{
+    void *buf;
+    buflen = virFileDirectAlign(buflen);
+
+# if WITH_POSIX_MEMALIGN
+    if (posix_memalign(alloc_base, VIR_FILE_DIRECT_ALIGN_MASK + 1, buflen)) {
+        *alloc_base = NULL;
+        return NULL;
+    }
+    buf = *alloc_base;
+# else
+    *alloc_base = g_malloc(buflen + VIR_FILE_DIRECT_ALIGN_MASK);
+    buf = virFileDirectAlign((uintptr_t)*alloc_base);
+# endif
+    return buf;
+}
+
 # ifdef __linux__
 
 /**
@@ -372,6 +536,16 @@ virFileWrapperFdNew(int *fd, const char *name, unsigned 
int flags)
     return NULL;
 }
 #else /* WIN32 */
+
+void *
+virFileDirectBufferNew(void **alloc_base G_GNUC_UNUSED,
+                       size_t buflen G_GNUC_UNUSED)
+{
+    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                   _("virFileDirectBufferNew unsupported on this platform"));
+    return NULL;
+}
+
 virFileWrapperFd *
 virFileWrapperFdNew(int *fd G_GNUC_UNUSED,
                     const char *name G_GNUC_UNUSED,
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 8e378efe30..844261e0a4 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -104,6 +104,15 @@ typedef struct _virFileWrapperFd virFileWrapperFd;
 
 int virFileDirectFdFlag(void);
 
+#define VIR_FILE_DIRECT_ALIGN_MASK ((64 * 1024) - 1)
+
+void *virFileDirectBufferNew(void **alloc_base, size_t buflen);
+uintptr_t virFileDirectAlign(uintptr_t value);
+ssize_t virFileDirectRead(int fd, void *buf, size_t count);
+ssize_t virFileDirectWrite(int fd, void *buf, size_t count);
+size_t virFileDirectCopyBuf(void **buf, size_t count, void **dst, size_t 
*dst_len);
+ssize_t virFileDirectReadCopy(int fd, void **buf, size_t buflen, void *dst, 
size_t dst_len);
+
 typedef enum {
     VIR_FILE_WRAPPER_BYPASS_CACHE   = (1 << 0),
     VIR_FILE_WRAPPER_NON_BLOCKING   = (1 << 1),
-- 
2.26.2

Reply via email to