1 /*
2  * Copyright 2017 Frank Hunleth
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef BLOCK_CACHE_H
18 #define BLOCK_CACHE_H
19 
20 #include "config.h"
21 #include "util.h"
22 
23 // Don't use pthreads on Windows yet.
24 #ifndef _WIN32
25 #if HAVE_PTHREAD
26 #define USE_PTHREADS 1
27 #endif
28 #endif
29 
30 #if USE_PTHREADS
31 #include <pthread.h>
32 #endif
33 
34 // The segment size defines the minimum read/write size
35 // actually made to the output. Additionally, all reads and
36 // writes will be aligned to that size.
37 #define BLOCK_CACHE_SEGMENT_SIZE       (128*1024) // This is also the read/write write size
38 #define BLOCK_CACHE_NUM_SEGMENTS       64         // 8 MB cache
39 #define BLOCK_CACHE_BLOCKS_PER_SEGMENT (BLOCK_CACHE_SEGMENT_SIZE / FWUP_BLOCK_SIZE)
40 #define BLOCK_CACHE_SEGMENT_MASK       (~(BLOCK_CACHE_SEGMENT_SIZE - 1))
41 
42 struct block_cache_segment {
43     bool in_use;
44 
45     // The data in this segment.
46     uint8_t *data;
47 
48     // Where this segment is located
49     off_t offset;
50 
51     // "timestamp" for computing least recently used
52     uint32_t last_access;
53 
54     // Set to true if all of the data written to this segment
55     // has been streamed. If true and the entire segment is marked
56     // dirty, then it should be written to the target asap so that
57     // the actual writes can start.
58     bool streamed;
59 
60     // Bit fields for determining whether blocks inside the
61     // segment are valid (hold the most up-to-date data) and/or
62     // dirty (need to be written back to the target image).
63     // (2 bits of flags in a uint8_t)
64     uint8_t flags[BLOCK_CACHE_BLOCKS_PER_SEGMENT * 2 / 8];
65 };
66 
67 struct block_cache {
68     int fd;
69 
70     // Read everything back after its written
71     bool verify_writes;
72 
73     // Counter for maintaining LRU
74     uint32_t timestamp;
75 
76     // All of the cached segments
77     struct block_cache_segment segments[BLOCK_CACHE_NUM_SEGMENTS];
78 
79     // Temporary buffer for reading segments that are partially valid
80     uint8_t *read_temp;
81 
82     // Temporary buffer for checking that writes worked
83     uint8_t *verify_temp;
84 
85     // Track "trimmed" segments in a bitfield. One bit per segment.
86     // E.g., 128K/bit -> 1M takes represented in 1 byte -> 1G in 1 KB, etc.
87     size_t trimmed_len;
88     off_t trimmed_end_offset;
89     uint8_t *trimmed;
90     bool trimmed_remainder; // true if segments after end of bitfield are trimmed
91     bool hw_trim_enabled;
92 
93     // This tracks the number of blocks on the destination
94     uint32_t num_blocks;
95 
96     // Asynchronous writes
97 #if USE_PTHREADS
98     pthread_t writer_thread;
99     pthread_mutex_t mutex;
100     pthread_cond_t cond;
101     uint8_t *thread_verify_temp;
102 
103     volatile bool running;
104     volatile struct block_cache_segment *seg_to_write;
105     volatile off_t bad_offset; // set if pwrite fails asynchronously
106 #endif
107 };
108 
109 int block_cache_init(struct block_cache *bc, int fd, off_t end_offset, bool enable_trim, bool verify_writes);
110 int block_cache_trim(struct block_cache *bc, off_t offset, off_t count, bool hwtrim);
111 int block_cache_trim_after(struct block_cache *bc, off_t offset, bool hwtrim);
112 int block_cache_pwrite(struct block_cache *bc, const void *buf, size_t count, off_t offset, bool streamed);
113 int block_cache_pread(struct block_cache *bc, void *buf, size_t count, off_t offset);
114 int block_cache_flush(struct block_cache *bc);
115 void block_cache_reset(struct block_cache *bc);
116 int block_cache_free(struct block_cache *bc);
117 
118 #endif // BLOCK_CACHE_H
119