dan | e795226 | 2018-07-16 20:44:00 +0000 | [diff] [blame] | 1 | |
| 2 | SQLite's OS layer contains the following definitions used in F2FS related |
| 3 | calls: |
| 4 | |
| 5 | #define F2FS_IOCTL_MAGIC 0xf5 |
| 6 | #define F2FS_IOC_START_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 1) |
| 7 | #define F2FS_IOC_COMMIT_ATOMIC_WRITE _IO(F2FS_IOCTL_MAGIC, 2) |
| 8 | #define F2FS_IOC_START_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 3) |
| 9 | #define F2FS_IOC_ABORT_VOLATILE_WRITE _IO(F2FS_IOCTL_MAGIC, 5) |
| 10 | #define F2FS_IOC_GET_FEATURES _IOR(F2FS_IOCTL_MAGIC, 12, u32) |
| 11 | #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 |
| 12 | |
| 13 | After opening a database file on Linux (including Android), SQLite determines |
| 14 | whether or not a file supports F2FS atomic commits as follows: |
| 15 | |
| 16 | u32 flags = 0; |
| 17 | rc = ioctl(fd, F2FS_IOC_GET_FEATURES, &flags); |
| 18 | if( rc==0 && (flags & F2FS_FEATURE_ATOMIC_WRITE) ){ |
| 19 | /* File supports F2FS atomic commits */ |
| 20 | }else{ |
| 21 | /* File does NOT support F2FS atomic commits */ |
| 22 | } |
| 23 | |
| 24 | where "fd" is the file-descriptor open on the database file. |
| 25 | |
| 26 | Usually, when writing to a database file that supports atomic commits, SQLite |
| 27 | accumulates the entire transaction in heap memory, deferring all writes to the |
| 28 | db file until the transaction is committed. |
| 29 | |
| 30 | When it is time to commit a transaction on a file that supports atomic |
| 31 | commits, SQLite does: |
| 32 | |
| 33 | /* Take an F_WRLCK lock on the database file. This prevents any other |
| 34 | ** SQLite clients from reading or writing the file until the lock |
| 35 | ** is released. */ |
| 36 | rc = fcntl(fd, F_SETLK, ...); |
| 37 | if( rc!=0 ) goto failed; |
| 38 | |
| 39 | rc = ioctl(fd, F2FS_IOC_START_ATOMIC_WRITE); |
| 40 | if( rc!=0 ) goto fallback_to_legacy_journal_commit; |
| 41 | |
| 42 | foreach (dirty page){ |
| 43 | rc = write(fd, ...dirty page...); |
| 44 | if( rc!=0 ){ |
| 45 | ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE); |
| 46 | goto fallback_to_legacy_journal_commit; |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | rc = ioctl(fd, F2FS_IOC_COMMIT_ATOMIC_WRITE); |
| 51 | if( rc!=0 ){ |
| 52 | ioctl(fd, F2FS_IOC_ABORT_VOLATILE_WRITE); |
| 53 | goto fallback_to_legacy_journal_commit; |
| 54 | } |
| 55 | |
| 56 | /* If we get there, the transaction has been successfully |
| 57 | ** committed to persistent storage. The following call |
| 58 | ** relinquishes the F_WRLCK lock. */ |
| 59 | fcntl(fd, F_SETLK, ...); |
| 60 | |
| 61 | Assumptions: |
| 62 | |
| 63 | 1. After either of the F2FS_IOC_ABORT_VOLATILE_WRITE calls return, |
| 64 | the database file is in the state that it was in before |
| 65 | F2FS_IOC_START_ATOMIC_WRITE was invoked. Even if the ioctl() |
| 66 | fails - we're ignoring the return code. |
| 67 | |
| 68 | This is true regardless of the type of error that occurred in |
| 69 | ioctl() or write(). |
| 70 | |
| 71 | 2. If the system fails before the F2FS_IOC_COMMIT_ATOMIC_WRITE is |
| 72 | completed, then following a reboot the database file is in the |
| 73 | state that it was in before F2FS_IOC_START_ATOMIC_WRITE was invoked. |
| 74 | Or, if the write was commited right before the system failed, in a |
| 75 | state indicating that all write() calls were successfully committed |
| 76 | to persistent storage before the failure occurred. |
| 77 | |
| 78 | 3. If the process crashes before the F2FS_IOC_COMMIT_ATOMIC_WRITE is |
| 79 | completed then the file is automatically restored to the state that |
| 80 | it was in before F2FS_IOC_START_ATOMIC_WRITE was called. This occurs |
| 81 | before the posix advisory lock is automatically dropped - there is |
| 82 | no chance that another client will be able to read the file in a |
| 83 | half-committed state before the rollback operation occurs. |
| 84 | |
| 85 | |
| 86 | |
| 87 | |