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 |   8 ++
 src/util/virfile.c       | 249 +++++++++++++++++++++++++++++++++++++++
 src/util/virfile.h       |  11 ++
 3 files changed, 268 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9309259751..2c4077ed38 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2236,7 +2236,15 @@ virFileComparePaths;
 virFileCopyACLs;
 virFileDataSync;
 virFileDeleteTree;
+virFileDirectAlign;
+virFileDirectBufferNew;
+virFileDirectCopyBuf;
 virFileDirectFdFlag;
+virFileDirectRead;
+virFileDirectReadCopy;
+virFileDirectReadLim;
+virFileDirectWrite;
+virFileDirectWriteLim;
 virFileExists;
 virFileFclose;
 virFileFdopen;
diff --git a/src/util/virfile.c b/src/util/virfile.c
index e4522b5f67..03a7cdc9bf 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -192,6 +192,210 @@ 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;
+}
+
+/**
+ * virFileDirectReadLim: perform multiple aligned reads up to limit
+ * @fd:    O_DIRECT file descriptor
+ * @buf:   aligned buffer to read into
+ * @limit: the desired limit to read into buffer.
+ *         Note that if unaligned, extra bytes will be read based on alignment.
+ *
+ * Note that buf should be able to contain limit plus the alignment!
+ *
+ * Compared with virFileDirectRead, this function reads potentially
+ * multiple times to fill up to buffer with up to limit bytes plus alignment,
+ * so on success it does not return a number of bytes smaller than limit.
+ *
+ * Returns < 0 and errno set on error,
+ * and on success the number of bytes read, which may be greater than limit
+ * due to alignment.
+ */
+ssize_t
+virFileDirectReadLim(int fd, void *buf, size_t lim)
+{
+    ssize_t nread = 0;
+
+    while (lim > 0) {
+        ssize_t r = virFileDirectRead(fd, buf, lim);
+        if (r < 0)
+            return r;
+        if (r == 0)
+            return nread;
+        buf = (char *)buf + r;
+        nread += r;
+        if (lim < r)
+            break;
+        lim -= r;
+    }
+    return nread;
+}
+
+/**
+ * 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 = virFileDirectReadLim(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;
+}
+
+/**
+ * virFileDirectWriteLim: perform multiple aligned writes up to limit
+ * @fd:    O_DIRECT file descriptor
+ * @buf:   aligned buffer to write from
+ * @limit: the desired limit for the total write size
+ *         Note that if unaligned, extra bytes will be written based on 
alignment.
+ *
+ * Note that buf should be able to contain limit plus the alignment!
+ *
+ * Compared with virFileDirectWrite, this function writes potentially
+ * multiple times to drain the buffer up to the limit bytes plus alignment,
+ * so on success it does not return a number of bytes smaller than limit.
+ *
+ * Returns < 0 and errno set on error,
+ * and on success the number of bytes written, which may be greater than limit
+ * due to alignment.
+ */
+
+ssize_t
+virFileDirectWriteLim(int fd, void *buf, size_t lim)
+{
+    ssize_t nwritten = 0;
+
+    while (lim > 0) {
+        ssize_t r = virFileDirectWrite(fd, buf, lim);
+        if (r < 0)
+            return r;
+        if (r == 0)
+            return nwritten;
+        buf = (char *)buf + r;
+        nwritten += r;
+        if (lim < r)
+            break;
+        lim -= r;
+    }
+    return nwritten;
+}
+
 /* Opaque type for managing a wrapper around a fd.  For now,
  * read-write is not supported, just a single direction.  */
 struct _virFileWrapperFd {
@@ -202,6 +406,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 +611,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..9af3dc5528 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -104,6 +104,17 @@ 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);
+ssize_t virFileDirectReadLim(int fd, void *buf, size_t lim);
+ssize_t virFileDirectWriteLim(int fd, void *buf, size_t lim);
+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.35.3

Reply via email to