}
 
 /*
- * Calculate the potential space needed by the log vector.  We may need a start
- * record, and each region gets its own struct xlog_op_header and may need to be
- * double word aligned.
+ * Calculate the potential space needed by the log vector. If this is a start
+ * transaction, the caller has already accounted for both opheaders in the start
+ * transaction, so we don't need to account for them here.
  */
 static int
 xlog_write_calc_vec_length(
        int                     len = 0;
        int                     i;
 
-       if (optype & XLOG_START_TRANS)
-               headers++;
-
        for (lv = log_vector; lv; lv = lv->lv_next) {
                /* we don't write ordered log vectors */
                if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED)
                }
        }
 
+       /* Don't account for regions with embedded ophdrs */
+       if (optype && headers > 0) {
+               if (optype & XLOG_START_TRANS) {
+                       ASSERT(headers >= 2);
+                       headers -= 2;
+               }
+       }
+
        ticket->t_res_num_ophdrs += headers;
        len += headers * sizeof(struct xlog_op_header);
 
        return len;
 }
 
-static void
-xlog_write_start_rec(
-       struct xlog_op_header   *ophdr,
-       struct xlog_ticket      *ticket)
-{
-       ophdr->oh_tid   = cpu_to_be32(ticket->t_tid);
-       ophdr->oh_clientid = ticket->t_clientid;
-       ophdr->oh_len = 0;
-       ophdr->oh_flags = XLOG_START_TRANS;
-       ophdr->oh_res2 = 0;
-}
-
 static xlog_op_header_t *
 xlog_write_setup_ophdr(
        struct xlog             *log,
         * If this is a commit or unmount transaction, we don't need a start
         * record to be written.  We do, however, have to account for the
         * commit or unmount header that gets written. Hence we always have
-        * to account for an extra xlog_op_header here.
+        * to account for an extra xlog_op_header here for commit and unmount
+        * records.
         */
-       ticket->t_curr_res -= sizeof(struct xlog_op_header);
+       if (optype & (XLOG_COMMIT_TRANS | XLOG_UNMOUNT_TRANS))
+               ticket->t_curr_res -= sizeof(struct xlog_op_header);
        if (ticket->t_curr_res < 0) {
                xfs_alert_tag(log->l_mp, XFS_PTAG_LOGRES,
                     "ctx ticket reservation ran out. Need to up reservation");
                        int                     copy_len;
                        int                     copy_off;
                        bool                    ordered = false;
-                       bool                    wrote_start_rec = false;
+                       bool                    added_ophdr = false;
 
                        /* ordered log vectors have no regions to write */
                        if (lv->lv_buf_len == XFS_LOG_VEC_ORDERED) {
                        ASSERT((unsigned long)ptr % sizeof(int32_t) == 0);
 
                        /*
-                        * Before we start formatting log vectors, we need to
-                        * write a start record. Only do this for the first
-                        * iclog we write to.
+                        * The XLOG_START_TRANS has embedded ophdrs for the
+                        * start record and transaction header. They will always
+                        * be the first two regions in the lv chain.
                         */
                        if (optype & XLOG_START_TRANS) {
-                               xlog_write_start_rec(ptr, ticket);
-                               xlog_write_adv_cnt(&ptr, &len, &log_offset,
-                                               sizeof(struct xlog_op_header));
-                               optype &= ~XLOG_START_TRANS;
-                               wrote_start_rec = true;
-                       }
-
-                       ophdr = xlog_write_setup_ophdr(log, ptr, ticket, optype);
-                       if (!ophdr)
-                               return -EIO;
+                               ophdr = reg->i_addr;
+                               if (index)
+                                       optype &= ~XLOG_START_TRANS;
+                       } else {
+                               ophdr = xlog_write_setup_ophdr(log, ptr,
+                                                       ticket, optype);
+                               if (!ophdr)
+                                       return -EIO;
 
-                       xlog_write_adv_cnt(&ptr, &len, &log_offset,
+                               xlog_write_adv_cnt(&ptr, &len, &log_offset,
                                           sizeof(struct xlog_op_header));
-
+                               added_ophdr = true;
+                       }
                        len += xlog_write_setup_copy(ticket, ophdr,
                                                     iclog->ic_size-log_offset,
                                                     reg->i_len,
                                                     &partial_copy_len);
                        xlog_verify_dest_ptr(log, ptr);
 
+
+                       /*
+                        * Wart: need to update length in embedded ophdr not
+                        * to include it's own length.
+                        */
+                       if (!added_ophdr) {
+                               ophdr->oh_len = cpu_to_be32(copy_len -
+                                               sizeof(struct xlog_op_header));
+                       }
                        /*
                         * Copy region.
                         *
-                        * Unmount records just log an opheader, so can have
-                        * empty payloads with no data region to copy. Hence we
-                        * only copy the payload if the vector says it has data
-                        * to copy.
+                        * Commit and unmount records just log an opheader, so
+                        * we can have empty payloads with no data region to
+                        * copy.  Hence we only copy the payload if the vector
+                        * says it has data to copy.
                         */
                        ASSERT(copy_len >= 0);
                        if (copy_len > 0) {
                                xlog_write_adv_cnt(&ptr, &len, &log_offset,
                                                   copy_len);
                        }
-                       copy_len += sizeof(struct xlog_op_header);
-                       record_cnt++;
-                       if (wrote_start_rec) {
+                       if (added_ophdr)
                                copy_len += sizeof(struct xlog_op_header);
-                               record_cnt++;
-                       }
+                       record_cnt++;
                        data_cnt += contwr ? copy_len : 0;
 
                        error = xlog_write_copy_finish(log, iclog, optype,
 
 }
 
 struct xlog_cil_trans_hdr {
+       struct xlog_op_header   oph[2];
        struct xfs_trans_header thdr;
-       struct xfs_log_iovec    lhdr;
+       struct xfs_log_iovec    lhdr[2];
 };
 
 /*
  * Build a checkpoint transaction header to begin the journal transaction.  We
  * need to account for the space used by the transaction header here as it is
  * not accounted for in xlog_write().
+ *
+ * This is the only place we write a transaction header, so we also build the
+ * log opheaders that indicate the start of a log transaction and wrap the
+ * transaction header. We keep the start record in it's own log vector rather
+ * than compacting them into a single region as this ends up making the logic
+ * in xlog_write() for handling empty opheaders for start, commit and unmount
+ * records much simpler.
  */
 static void
 xlog_cil_build_trans_hdr(
        int                     num_iovecs)
 {
        struct xlog_ticket      *tic = ctx->ticket;
+       __be32                  tid = cpu_to_be32(tic->t_tid);
 
        memset(hdr, 0, sizeof(*hdr));
 
+       /* Log start record */
+       hdr->oph[0].oh_tid = tid;
+       hdr->oph[0].oh_clientid = XFS_TRANSACTION;
+       hdr->oph[0].oh_flags = XLOG_START_TRANS;
+
+       /* log iovec region pointer */
+       hdr->lhdr[0].i_addr = &hdr->oph[0];
+       hdr->lhdr[0].i_len = sizeof(struct xlog_op_header);
+       hdr->lhdr[0].i_type = XLOG_REG_TYPE_LRHEADER;
+
+       /* log opheader */
+       hdr->oph[1].oh_tid = tid;
+       hdr->oph[1].oh_clientid = XFS_TRANSACTION;
+       hdr->oph[1].oh_len = cpu_to_be32(sizeof(struct xfs_trans_header));
+
+       /* transaction header in host byte order format */
        hdr->thdr.th_magic = XFS_TRANS_HEADER_MAGIC;
        hdr->thdr.th_type = XFS_TRANS_CHECKPOINT;
        hdr->thdr.th_tid = tic->t_tid;
        hdr->thdr.th_num_items = num_iovecs;
-       hdr->lhdr.i_addr = &hdr->thdr;
-       hdr->lhdr.i_len = sizeof(xfs_trans_header_t);
-       hdr->lhdr.i_type = XLOG_REG_TYPE_TRANSHDR;
-       tic->t_curr_res -= hdr->lhdr.i_len + sizeof(struct xlog_op_header);
 
-       lvhdr->lv_niovecs = 1;
-       lvhdr->lv_iovecp = &hdr->lhdr;
+       /* log iovec region pointer */
+       hdr->lhdr[1].i_addr = &hdr->oph[1];
+       hdr->lhdr[1].i_len = sizeof(struct xlog_op_header) +
+                               sizeof(struct xfs_trans_header);
+       hdr->lhdr[1].i_type = XLOG_REG_TYPE_TRANSHDR;
+
+       tic->t_curr_res -= hdr->lhdr[0].i_len + hdr->lhdr[1].i_len;
+
+       lvhdr->lv_niovecs = 2;
+       lvhdr->lv_iovecp = &hdr->lhdr[0];
        lvhdr->lv_next = ctx->lv_chain;
 }