The branch main has been updated by rlibby:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=a2fda816eb054d5873be223ef2461741dfcc253c

commit a2fda816eb054d5873be223ef2461741dfcc253c
Author:     Ryan Libby <rli...@freebsd.org>
AuthorDate: 2024-06-11 00:36:20 +0000
Commit:     Ryan Libby <rli...@freebsd.org>
CommitDate: 2024-06-11 00:36:20 +0000

    virstor: write large maps in chunks during label
    
    During the initial label of a virstor device, write out the allocation
    map in chunks if it is large (> 1 MB) in order to avoid large mallocs.
    
    Even though the kernel virstor geom may still do a large malloc to
    represent the allocation map, this may still be useful to avoid a
    ulimit.
    
    Reviewed by:    markj
    Sponsored by:   Dell EMC Isilon
    Differential Revision:  https://reviews.freebsd.org/D45517
---
 lib/geom/virstor/geom_virstor.c | 59 ++++++++++++++++++++++++++++++-----------
 1 file changed, 43 insertions(+), 16 deletions(-)

diff --git a/lib/geom/virstor/geom_virstor.c b/lib/geom/virstor/geom_virstor.c
index 131ece0107c7..6a7dfb27fe43 100644
--- a/lib/geom/virstor/geom_virstor.c
+++ b/lib/geom/virstor/geom_virstor.c
@@ -157,8 +157,7 @@ virstor_label(struct gctl_req *req)
        char param[32];
        int hardcode, nargs, error;
        struct virstor_map_entry *map;
-       size_t total_chunks;    /* We'll run out of memory if
-                                  this needs to be bigger. */
+       size_t total_chunks, write_max_map_entries;
        unsigned int map_chunks; /* Chunks needed by the map (map size). */
        size_t map_size;        /* In bytes. */
        ssize_t written;
@@ -325,28 +324,56 @@ virstor_label(struct gctl_req *req)
                sprintf(param, "%s%s", _PATH_DEV, name);
                fd = open(param, O_RDWR);
        }
-       if (fd < 0)
+       if (fd < 0) {
                gctl_error(req, "Cannot open provider %s to write map", name);
+               return;
+       }
 
-       /* Do it with calloc because there might be a need to set up chunk flags
-        * in the future */
-       map = calloc(total_chunks, sizeof(*map));
+       /*
+        * Initialize and write the map.  Don't malloc the whole map at once,
+        * in case it's large.  Use calloc because there might be a need to set
+        * up chunk flags in the future.
+        */
+       write_max_map_entries = 1024 * 1024 / sizeof(*map);
+       if (write_max_map_entries > total_chunks)
+               write_max_map_entries = total_chunks;
+       map = calloc(write_max_map_entries, sizeof(*map));
        if (map == NULL) {
                gctl_error(req,
                    "Out of memory (need %zu bytes for allocation map)",
-                   map_size);
+                   write_max_map_entries * sizeof(*map));
+               close(fd);
+               return;
        }
-
-       written = pwrite(fd, map, map_size, 0);
-       free(map);
-       if ((size_t)written != map_size) {
-               if (verbose) {
-                       fprintf(stderr, "\nTried to write %zu, written %zd 
(%s)\n",
-                           map_size, written, strerror(errno));
+       for (size_t chunk = 0; chunk < total_chunks;
+           chunk += write_max_map_entries) {
+               size_t bytes_to_write, entries_to_write;
+
+               entries_to_write = total_chunks - chunk;
+               if (entries_to_write > write_max_map_entries)
+                       entries_to_write = write_max_map_entries;
+               bytes_to_write = entries_to_write * sizeof(*map);
+               for (size_t off = 0; off < bytes_to_write; off += written) {
+                       written = write(fd, ((char *)map) + off,
+                           bytes_to_write - off);
+                       if (written < 0) {
+                               if (verbose) {
+                                       fprintf(stderr,
+                                           "\nError writing map at offset "
+                                           "%zu of %zu: %s\n",
+                                           chunk * sizeof(*map) + off,
+                                           map_size, strerror(errno));
+                               }
+                               gctl_error(req,
+                                   "Error writing out allocation map!");
+                               free(map);
+                               close(fd);
+                               return;
+                       }
                }
-               gctl_error(req, "Error writing out allocation map!");
-               return;
        }
+       free(map);
+       map = NULL;
        close (fd);
 
        if (verbose)

Reply via email to