Hi,
Here is the first patch to bring Journaling to OpenBSD based on the McKusick
paper: https://www.bsdcan.org/2010/schedule/attachments/141_suj-slides.pd and
FreeBSD 10
This first patch is just for structures and definitions.
I know is my first patch suggestion and this feature is not usual, but I am
fully focused on making it work, and I hope to receive help from expert
developers.
Walter Neto
Index: sys/sys/mount.h
===================================================================
RCS file: /Volumes/CSP/cvs/src/sys/sys/mount.h,v
retrieving revision 1.121
diff -u -u -r1.121 mount.h
--- sys/sys/mount.h 8 Sep 2014 01:47:06 -0000 1.121
+++ sys/sys/mount.h 1 Sep 2015 20:23:03 -0000
@@ -413,6 +413,7 @@
#define MNT_WANTRDWR 0x02000000 /* want upgrade to read/write */
#define MNT_SOFTDEP 0x04000000 /* soft dependencies being done */
#define MNT_DOOMED 0x08000000 /* device behind filesystem is gone */
+#define MNT_SUJ 0x10000000 /* using journaled soft updates
*/
/*
* Flags for various system call interfaces.
Index: sys/ufs/ffs/softdep.h
===================================================================
RCS file: /Volumes/CSP/cvs/src/sys/ufs/ffs/softdep.h,v
retrieving revision 1.17
diff -u -u -r1.17 softdep.h
--- sys/ufs/ffs/softdep.h 11 Jun 2013 16:42:18 -0000 1.17
+++ sys/ufs/ffs/softdep.h 1 Sep 2015 20:23:03 -0000
@@ -165,6 +165,12 @@
LIST_HEAD(allocindirhd, allocindir);
LIST_HEAD(allocdirecthd, allocdirect);
TAILQ_HEAD(allocdirectlst, allocdirect);
+LIST_HEAD(jaddrefhd, jaddref);
+LIST_HEAD(jremrefhd, jremref);
+LIST_HEAD(jmvrefhd, jmvref);
+LIST_HEAD(jnewblkhd, jnewblk);
+LIST_HEAD(jblkdephd, jblkdep);
+TAILQ_HEAD(jseglst, jseg);
/*
* The "pagedep" structure tracks the various dependencies related to
@@ -587,3 +593,246 @@
# define db_state db_list.wk_state /* unused */
struct pagedep *db_pagedep;/* associated pagedep */
};
+
+/*
+ * The inoref structure holds the elements common to jaddref and jremref
+ * so they may easily be queued in-order on the inodedep.
+ */
+struct inoref {
+ struct worklist if_list; /* Journal pending or jseg entries. */
+# define if_state if_list.wk_state
+ TAILQ_ENTRY(inoref) if_deps; /* Links for inodedep. */
+ struct jsegdep *if_jsegdep; /* Will track our journal record. */
+ doff_t if_diroff; /* Directory offset. */
+ ufsino_t if_ino; /* Inode number. */
+ ufsino_t if_parent; /* Parent inode number. */
+ nlink_t if_nlink; /* nlink before addition. */
+ uint16_t if_mode; /* File mode, needed for IFMT. */
+};
+
+/*
+ * A "jaddref" structure tracks a new reference (link count) on an inode
+ * and prevents the link count increase and bitmap allocation until a
+ * journal entry can be written. Once the journal entry is written,
+ * the inode is put on the pendinghd of the bmsafemap and a diradd or
+ * mkdir entry is placed on the bufwait list of the inode. The DEPCOMPLETE
+ * flag is used to indicate that all of the required information for writing
+ * the journal entry is present. MKDIR_BODY and MKDIR_PARENT are used to
+ * differentiate . and .. links from regular file names. NEWBLOCK indicates
+ * a bitmap is still pending. If a new reference is canceled by a delete
+ * prior to writing the journal the jaddref write is canceled and the
+ * structure persists to prevent any disk-visible changes until it is
+ * ultimately released when the file is freed or the link is dropped again.
+ */
+struct jaddref {
+ struct inoref ja_ref; /* see inoref above. */
+# define ja_list ja_ref.if_list /* Jrnl pending, id_inowait, dm_jwork.*/
+# define ja_state ja_ref.if_list.wk_state
+ LIST_ENTRY(jaddref) ja_bmdeps; /* Links for bmsafemap. */
+ union {
+ struct diradd *jau_diradd; /* Pending diradd. */
+ struct mkdir *jau_mkdir; /* MKDIR_{PARENT,BODY} */
+ } ja_un;
+};
+#define ja_diradd ja_un.jau_diradd
+#define ja_mkdir ja_un.jau_mkdir
+#define ja_diroff ja_ref.if_diroff
+#define ja_ino ja_ref.if_ino
+#define ja_parent ja_ref.if_parent
+#define ja_mode ja_ref.if_mode
+
+/*
+ * A "jremref" structure tracks a removed reference (unlink) on an
+ * inode and prevents the directory remove from proceeding until the
+ * journal entry is written. Once the journal has been written the remove
+ * may proceed as normal.
+ */
+struct jremref {
+ struct inoref jr_ref; /* see inoref above. */
+# define jr_list jr_ref.if_list /* Linked to softdep_journal_pending. */
+# define jr_state jr_ref.if_list.wk_state
+ LIST_ENTRY(jremref) jr_deps; /* Links for dirrem. */
+ struct dirrem *jr_dirrem; /* Back pointer to dirrem. */
+};
+
+/*
+ * A "jmvref" structure tracks a name relocations within the same
+ * directory block that occur as a result of directory compaction.
+ * It prevents the updated directory entry from being written to disk
+ * until the journal entry is written. Once the journal has been
+ * written the compacted directory may be written to disk.
+ */
+struct jmvref {
+ struct worklist jm_list; /* Linked to softdep_journal_pending. */
+ LIST_ENTRY(jmvref) jm_deps; /* Jmvref on pagedep. */
+ struct pagedep *jm_pagedep; /* Back pointer to pagedep. */
+ ufsino_t jm_parent; /* Containing directory inode number. */
+ ufsino_t jm_ino; /* Inode number of our entry. */
+ doff_t jm_oldoff; /* Our old offset in directory. */
+ doff_t jm_newoff; /* Our new offset in directory. */
+};
+
+/*
+ * A "jnewblk" structure tracks a newly allocated block or fragment and
+ * prevents the direct or indirect block pointer as well as the cg bitmap
+ * from being written until it is logged. After it is logged the jsegdep
+ * is attached to the allocdirect or allocindir until the operation is
+ * completed or reverted. If the operation is reverted prior to the journal
+ * write the jnewblk structure is maintained to prevent the bitmaps from
+ * reaching the disk. Ultimately the jnewblk structure will be passed
+ * to the free routine as the in memory cg is modified back to the free
+ * state at which time it can be released. It may be held on any of the
+ * fx_jwork, fw_jwork, fb_jwork, ff_jwork, nb_jwork, or ir_jwork lists.
+ */
+struct jnewblk {
+ struct worklist jn_list; /* See lists above. */
+# define jn_state jn_list.wk_state
+ struct jsegdep *jn_jsegdep; /* Will track our journal record. */
+ LIST_ENTRY(jnewblk) jn_deps; /* Jnewblks on sm_jnewblkhd. */
+ struct worklist *jn_dep; /* Dependency to ref completed seg. */
+ daddr_t jn_lbn; /* Lbn to which allocated. */
+ daddr_t jn_blkno; /* Blkno allocated */
+ ufsino_t jn_ino; /* Ino to which allocated. */
+ int jn_oldfrags; /* Previous fragments when extended. */
+ int jn_frags; /* Number of fragments. */
+};
+
+/*
+ * A "jblkdep" structure tracks jfreeblk and jtrunc records attached to a
+ * freeblks structure.
+ */
+struct jblkdep {
+ struct worklist jb_list; /* For softdep journal pending. */
+ struct jsegdep *jb_jsegdep; /* Reference to the jseg. */
+ struct freeblks *jb_freeblks; /* Back pointer to freeblks. */
+ LIST_ENTRY(jblkdep) jb_deps; /* Dep list on freeblks. */
+
+};
+
+/*
+ * A "jfreeblk" structure tracks the journal write for freeing a block
+ * or tree of blocks. The block pointer must not be cleared in the inode
+ * or indirect prior to the jfreeblk being written to the journal.
+ */
+struct jfreeblk {
+ struct jblkdep jf_dep; /* freeblks linkage. */
+ daddr_t jf_lbn; /* Lbn from which blocks freed. */
+ daddr_t jf_blkno; /* Blkno being freed. */
+ ufsino_t jf_ino; /* Ino from which blocks freed. */
+ int jf_frags; /* Number of frags being freed. */
+};
+
+/*
+ * A "jfreefrag" tracks the freeing of a single block when a fragment is
+ * extended or an indirect page is replaced. It is not part of a larger
+ * freeblks operation.
+ */
+struct jfreefrag {
+ struct worklist fr_list; /* Linked to softdep_journal_pending. */
+# define fr_state fr_list.wk_state
+ struct jsegdep *fr_jsegdep; /* Will track our journal record. */
+ struct freefrag *fr_freefrag; /* Back pointer to freefrag. */
+ daddr_t fr_lbn; /* Lbn from which frag freed. */
+ daddr_t fr_blkno; /* Blkno being freed. */
+ ufsino_t fr_ino; /* Ino from which frag freed. */
+ int fr_frags; /* Size of frag being freed. */
+};
+
+/*
+ * A "jtrunc" journals the intent to truncate an inode's data or extent area.
+ */
+struct jtrunc {
+ struct jblkdep jt_dep; /* freeblks linkage. */
+ off_t jt_size; /* Final file size. */
+ int jt_extsize; /* Final extent size. */
+ ufsino_t jt_ino; /* Ino being truncated. */
+};
+
+/*
+ * A "jfsync" journals the completion of an fsync which invalidates earlier
+ * jtrunc records in the journal.
+ */
+struct jfsync {
+ struct worklist jfs_list; /* For softdep journal pending. */
+ off_t jfs_size; /* Sync file size. */
+ int jfs_extsize; /* Sync extent size. */
+ ufsino_t jfs_ino; /* ino being synced. */
+};
+
+/*
+ * A "jsegdep" structure tracks a single reference to a written journal
+ * segment so the journal space can be reclaimed when all dependencies
+ * have been written. It can hang off of id_inowait, dm_jwork, da_jwork,
+ * nb_jwork, ff_jwork, or fb_jwork lists.
+ */
+struct jsegdep {
+ struct worklist jd_list; /* See above for lists. */
+# define jd_state jd_list.wk_state
+ struct jseg *jd_seg; /* Our journal record. */
+};
+
+/*
+ * A "jseg" structure contains all of the journal records written in a
+ * single disk write. The jaddref and jremref structures are linked into
+ * js_entries so thay may be completed when the write completes. The
+ * js_entries also include the write dependency structures: jmvref,
+ * jnewblk, jfreeblk, jfreefrag, and jtrunc. The js_refs field counts
+ * the number of entries on the js_entries list. Thus there is a single
+ * jseg entry to describe each journal write.
+ */
+struct jseg {
+ struct worklist js_list; /* b_deps link for journal */
+# define js_state js_list.wk_state
+ struct workhead js_entries; /* Entries awaiting write */
+ LIST_HEAD(, freework) js_indirs;/* List of indirects in this seg. */
+ TAILQ_ENTRY(jseg) js_next; /* List of all unfinished segments. */
+ struct jblocks *js_jblocks; /* Back pointer to block/seg list */
+ struct buf *js_buf; /* Buffer while unwritten */
+ uint64_t js_seq; /* Journal record sequence number. */
+ uint64_t js_oldseq; /* Oldest valid sequence number. */
+ int js_size; /* Size of journal record in bytes. */
+ int js_cnt; /* Total items allocated. */
+ int js_refs; /* Count of js_entries items. */
+};
+
+/*
+ * A 'sbdep' structure tracks the head of the free inode list and
+ * superblock writes. This makes sure the superblock is always pointing at
+ * the first possible unlinked inode for the suj recovery process. If a
+ * block write completes and we discover a new head is available the buf
+ * is dirtied and the dep is kept. See the description of the UNLINK*
+ * flags above for more details.
+ */
+struct sbdep {
+ struct worklist sb_list; /* b_dep linkage */
+ struct fs *sb_fs; /* Filesystem pointer within buf. */
+ struct ufsmount *sb_ump; /* Our mount structure */
+};
+
+/*
+ * Private journaling structures.
+ */
+struct jblocks {
+ struct jseglst jb_segs; /* TAILQ of current segments. */
+ struct jseg *jb_writeseg; /* Next write to complete. */
+ struct jseg *jb_oldestseg; /* Oldest segment with valid entries. */
+ struct jextent *jb_extent; /* Extent array. */
+ uint64_t jb_nextseq; /* Next sequence number. */
+ uint64_t jb_oldestwrseq; /* Oldest written sequence number. */
+ uint8_t jb_needseg; /* Need a forced segment. */
+ uint8_t jb_suspended; /* Did journal suspend writes? */
+ int jb_avail; /* Available extents. */
+ int jb_used; /* Last used extent. */
+ int jb_head; /* Allocator head. */
+ int jb_off; /* Allocator extent offset. */
+ int jb_blocks; /* Total disk blocks covered. */
+ int jb_free; /* Total disk blocks free. */
+ int jb_min; /* Minimum free space. */
+ int jb_low; /* Low on space. */
+ int jb_age; /* Insertion time of oldest rec. */
+};
+
+struct jextent {
+ daddr_t je_daddr; /* Disk block address. */
+ int je_blocks; /* Disk block count. */
+};