]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/xfs/xfs_log_cil.c
xfs: Reduce allocations during CIL insertion
[karo-tx-linux.git] / fs / xfs / xfs_log_cil.c
index 4e108720a789aa324adc48348bbf64272faa07a4..423ceaf0aeb03e2508e1a52aa5819051678a88b6 100644 (file)
@@ -80,6 +80,36 @@ xlog_cil_init_post_recovery(
                                                                log->l_curr_block);
 }
 
+STATIC int
+xlog_cil_lv_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       int     index;
+       char    *ptr;
+
+       /* format new vectors into array */
+       lip->li_ops->iop_format(lip, lv->lv_iovecp);
+
+       /* copy data into existing array */
+       ptr = lv->lv_buf;
+       for (index = 0; index < lv->lv_niovecs; index++) {
+               struct xfs_log_iovec *vec = &lv->lv_iovecp[index];
+
+               memcpy(ptr, vec->i_addr, vec->i_len);
+               vec->i_addr = ptr;
+               ptr += vec->i_len;
+       }
+
+       /*
+        * some size calculations for log vectors over-estimate, so the caller
+        * doesn't know the amount of space actually used by the item. Return
+        * the byte count to the caller so they can check and store it
+        * appropriately.
+        */
+       return ptr - lv->lv_buf;
+}
+
 /*
  * Format log item into a flat buffers
  *
@@ -111,7 +141,7 @@ xlog_cil_prepare_log_vecs(
        struct xfs_trans        *tp)
 {
        struct xfs_log_item_desc *lidp;
-       struct xfs_log_vec      *lv = NULL;
+       struct xfs_log_vec      *prev_lv = NULL;
        struct xfs_log_vec      *ret_lv = NULL;
 
 
@@ -123,12 +153,10 @@ xlog_cil_prepare_log_vecs(
 
        list_for_each_entry(lidp, &tp->t_items, lid_trans) {
                struct xfs_log_item *lip = lidp->lid_item;
-               struct xfs_log_vec *new_lv;
-               void    *ptr;
-               int     index;
-               int     len = 0;
-               uint    niovecs = 0;
-               uint    nbytes = 0;
+               struct xfs_log_vec *lv;
+               int     niovecs = 0;
+               int     nbytes = 0;
+               int     buf_size;
                bool    ordered = false;
 
                /* Skip items which aren't dirty in this transaction. */
@@ -150,48 +178,39 @@ xlog_cil_prepare_log_vecs(
                if (niovecs == XFS_LOG_VEC_ORDERED) {
                        ordered = true;
                        niovecs = 0;
+                       nbytes = 0;
                }
 
-               new_lv = kmem_zalloc(sizeof(*new_lv) +
-                               niovecs * sizeof(struct xfs_log_iovec),
-                               KM_SLEEP|KM_NOFS);
+               /* calc buffer size */
+               buf_size = sizeof(struct xfs_log_vec) + nbytes +
+                               niovecs * sizeof(struct xfs_log_iovec);
 
-               new_lv->lv_item = lip;
-               new_lv->lv_niovecs = niovecs;
+               /* allocate new data chunk */
+               lv = kmem_zalloc(buf_size, KM_SLEEP|KM_NOFS);
+               lv->lv_item = lip;
+               lv->lv_size = buf_size;
+               lv->lv_niovecs = niovecs;
                if (ordered) {
                        /* track as an ordered logvec */
-                       new_lv->lv_buf_len = XFS_LOG_VEC_ORDERED;
-                       goto next;
+                       ASSERT(lip->li_lv == NULL);
+                       lv->lv_buf_len = XFS_LOG_VEC_ORDERED;
+                       goto insert;
                }
 
                /* The allocated iovec region lies beyond the log vector. */
-               new_lv->lv_iovecp = (struct xfs_log_iovec *)&new_lv[1];
-
-               /* build the vector array and calculate it's length */
-               IOP_FORMAT(new_lv->lv_item, new_lv->lv_iovecp);
-               for (index = 0; index < new_lv->lv_niovecs; index++)
-                       len += new_lv->lv_iovecp[index].i_len;
+               lv->lv_iovecp = (struct xfs_log_iovec *)&lv[1];
 
-               new_lv->lv_buf_len = len;
-               new_lv->lv_buf = kmem_alloc(new_lv->lv_buf_len,
-                               KM_SLEEP|KM_NOFS);
-               ptr = new_lv->lv_buf;
-
-               for (index = 0; index < new_lv->lv_niovecs; index++) {
-                       struct xfs_log_iovec *vec = &new_lv->lv_iovecp[index];
-
-                       memcpy(ptr, vec->i_addr, vec->i_len);
-                       vec->i_addr = ptr;
-                       ptr += vec->i_len;
-               }
-               ASSERT(ptr == new_lv->lv_buf + new_lv->lv_buf_len);
+               /* The allocated data region lies beyond the iovec region */
+               lv->lv_buf = (char *)lv + buf_size - nbytes;
 
-next:
+               lv->lv_buf_len = xlog_cil_lv_item_format(lip, lv);
+               ASSERT(lv->lv_buf_len <= nbytes);
+insert:
                if (!ret_lv)
-                       ret_lv = new_lv;
+                       ret_lv = lv;
                else
-                       lv->lv_next = new_lv;
-               lv = new_lv;
+                       prev_lv->lv_next = lv;
+               prev_lv = lv;
        }
 
        return ret_lv;
@@ -228,7 +247,6 @@ xfs_cil_prepare_item(
 
                *len += lv->lv_buf_len - old->lv_buf_len;
                *diff_iovecs += lv->lv_niovecs - old->lv_niovecs;
-               kmem_free(old->lv_buf);
                kmem_free(old);
        } else {
                /* new lv, must pin the log item */
@@ -354,7 +372,6 @@ xlog_cil_free_logvec(
 
        for (lv = log_vector; lv; ) {
                struct xfs_log_vec *next = lv->lv_next;
-               kmem_free(lv->lv_buf);
                kmem_free(lv);
                lv = next;
        }