]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcache/buckets.h
8194dd9b6baf8101a1a066c89677ee65eaf4d93f
[bcachefs-tools-debian] / libbcache / buckets.h
1 /*
2  * Code for manipulating bucket marks for garbage collection.
3  *
4  * Copyright 2014 Datera, Inc.
5  */
6
7 #ifndef _BUCKETS_H
8 #define _BUCKETS_H
9
10 #include "buckets_types.h"
11 #include "super.h"
12
13 #define for_each_bucket(b, ca)                                  \
14         for (b = (ca)->buckets + (ca)->mi.first_bucket;         \
15              b < (ca)->buckets + (ca)->mi.nbuckets; b++)
16
17 #define bucket_cmpxchg(g, new, expr)                            \
18 ({                                                              \
19         u64 _v = READ_ONCE((g)->_mark.counter);                 \
20         struct bucket_mark _old;                                \
21                                                                 \
22         do {                                                    \
23                 (new).counter = _old.counter = _v;              \
24                 expr;                                           \
25         } while ((_v = cmpxchg(&(g)->_mark.counter,             \
26                                _old.counter,                    \
27                                (new).counter)) != _old.counter);\
28         _old;                                                   \
29 })
30
31 /*
32  * bucket_gc_gen() returns the difference between the bucket's current gen and
33  * the oldest gen of any pointer into that bucket in the btree.
34  */
35
36 static inline u8 bucket_gc_gen(struct cache *ca, struct bucket *g)
37 {
38         unsigned long r = g - ca->buckets;
39         return g->mark.gen - ca->oldest_gens[r];
40 }
41
42 static inline struct cache *PTR_CACHE(const struct cache_set *c,
43                                       const struct bch_extent_ptr *ptr)
44 {
45         EBUG_ON(ptr->dev > rcu_dereference(c->members)->nr_devices);
46
47         return rcu_dereference(c->cache[ptr->dev]);
48 }
49
50 static inline size_t PTR_BUCKET_NR(const struct cache *ca,
51                                    const struct bch_extent_ptr *ptr)
52 {
53         return sector_to_bucket(ca, ptr->offset);
54 }
55
56 /*
57  * Returns 0 if no pointers or device offline - only for tracepoints!
58  */
59 static inline size_t PTR_BUCKET_NR_TRACE(const struct cache_set *c,
60                                          const struct bkey_i *k,
61                                          unsigned ptr)
62 {
63         size_t bucket = 0;
64 #if 0
65         if (bkey_extent_is_data(&k->k)) {
66                 const struct bch_extent_ptr *ptr;
67                 const struct cache *ca;
68
69                 rcu_read_lock();
70                 extent_for_each_online_device(c, bkey_i_to_s_c_extent(k), ptr, ca) {
71                         bucket = PTR_BUCKET_NR(ca, ptr);
72                         break;
73                 }
74                 rcu_read_unlock();
75         }
76 #endif
77         return bucket;
78 }
79
80 static inline struct bucket *PTR_BUCKET(const struct cache *ca,
81                                         const struct bch_extent_ptr *ptr)
82 {
83         return ca->buckets + PTR_BUCKET_NR(ca, ptr);
84 }
85
86 static inline u8 __gen_after(u8 a, u8 b)
87 {
88         u8 r = a - b;
89
90         return r > 128U ? 0 : r;
91 }
92
93 static inline u8 gen_after(u8 a, u8 b)
94 {
95         u8 r = a - b;
96
97         BUG_ON(r > 128U);
98
99         return r;
100 }
101
102 /**
103  * ptr_stale() - check if a pointer points into a bucket that has been
104  * invalidated.
105  *
106  * Warning: PTR_CACHE(c, k, ptr) must equal ca.
107  */
108 static inline u8 ptr_stale(const struct cache *ca,
109                            const struct bch_extent_ptr *ptr)
110 {
111         return gen_after(PTR_BUCKET(ca, ptr)->mark.gen, ptr->gen);
112 }
113
114 /* bucket heaps */
115
116 static inline bool bucket_min_cmp(struct bucket_heap_entry l,
117                                   struct bucket_heap_entry r)
118 {
119         return l.val < r.val;
120 }
121
122 static inline bool bucket_max_cmp(struct bucket_heap_entry l,
123                                   struct bucket_heap_entry r)
124 {
125         return l.val > r.val;
126 }
127
128 static inline void bucket_heap_push(struct cache *ca, struct bucket *g,
129                                     unsigned long val)
130 {
131         struct bucket_heap_entry new = { g, val };
132
133         if (!heap_full(&ca->heap))
134                 heap_add(&ca->heap, new, bucket_min_cmp);
135         else if (bucket_min_cmp(new, heap_peek(&ca->heap))) {
136                 ca->heap.data[0] = new;
137                 heap_sift(&ca->heap, 0, bucket_min_cmp);
138         }
139 }
140
141 /* bucket gc marks */
142
143 /* The dirty and cached sector counts saturate. If this occurs,
144  * reference counting alone will not free the bucket, and a btree
145  * GC must be performed. */
146 #define GC_MAX_SECTORS_USED ((1U << 15) - 1)
147
148 static inline bool bucket_unused(struct bucket *g)
149 {
150         return !g->mark.counter;
151 }
152
153 static inline unsigned bucket_sectors_used(struct bucket *g)
154 {
155         return g->mark.dirty_sectors + g->mark.cached_sectors;
156 }
157
158 /* Per device stats: */
159
160 struct bucket_stats_cache __bch_bucket_stats_read_cache(struct cache *);
161 struct bucket_stats_cache bch_bucket_stats_read_cache(struct cache *);
162
163 static inline u64 __buckets_available_cache(struct cache *ca,
164                                             struct bucket_stats_cache stats)
165 {
166         return max_t(s64, 0,
167                      ca->mi.nbuckets - ca->mi.first_bucket -
168                      stats.buckets_dirty -
169                      stats.buckets_alloc -
170                      stats.buckets_meta);
171 }
172
173 /*
174  * Number of reclaimable buckets - only for use by the allocator thread:
175  */
176 static inline u64 buckets_available_cache(struct cache *ca)
177 {
178         return __buckets_available_cache(ca, bch_bucket_stats_read_cache(ca));
179 }
180
181 static inline u64 __buckets_free_cache(struct cache *ca,
182                                        struct bucket_stats_cache stats)
183 {
184         return __buckets_available_cache(ca, stats) +
185                 fifo_used(&ca->free[RESERVE_NONE]) +
186                 fifo_used(&ca->free_inc);
187 }
188
189 static inline u64 buckets_free_cache(struct cache *ca)
190 {
191         return __buckets_free_cache(ca, bch_bucket_stats_read_cache(ca));
192 }
193
194 /* Cache set stats: */
195
196 struct bucket_stats_cache_set __bch_bucket_stats_read_cache_set(struct cache_set *);
197 struct bucket_stats_cache_set bch_bucket_stats_read_cache_set(struct cache_set *);
198 void bch_cache_set_stats_apply(struct cache_set *,
199                                struct bucket_stats_cache_set *,
200                                struct disk_reservation *,
201                                struct gc_pos);
202
203 static inline u64 __cache_set_sectors_used(struct cache_set *c)
204 {
205         struct bucket_stats_cache_set stats = __bch_bucket_stats_read_cache_set(c);
206         u64 reserved = stats.persistent_reserved +
207                 stats.online_reserved;
208
209         return stats.s[S_COMPRESSED][S_META] +
210                 stats.s[S_COMPRESSED][S_DIRTY] +
211                 reserved +
212                 (reserved >> 7);
213 }
214
215 static inline u64 cache_set_sectors_used(struct cache_set *c)
216 {
217         return min(c->capacity, __cache_set_sectors_used(c));
218 }
219
220 /* XXX: kill? */
221 static inline u64 sectors_available(struct cache_set *c)
222 {
223         struct cache *ca;
224         unsigned i;
225         u64 ret = 0;
226
227         rcu_read_lock();
228         for_each_cache_rcu(ca, c, i)
229                 ret += buckets_available_cache(ca) << ca->bucket_bits;
230         rcu_read_unlock();
231
232         return ret;
233 }
234
235 static inline bool is_available_bucket(struct bucket_mark mark)
236 {
237         return (!mark.owned_by_allocator &&
238                 !mark.is_metadata &&
239                 !mark.dirty_sectors);
240 }
241
242 void bch_bucket_seq_cleanup(struct cache_set *);
243
244 void bch_invalidate_bucket(struct cache *, struct bucket *);
245 void bch_mark_free_bucket(struct cache *, struct bucket *);
246 void bch_mark_alloc_bucket(struct cache *, struct bucket *, bool);
247 void bch_mark_metadata_bucket(struct cache *, struct bucket *, bool);
248
249 void __bch_gc_mark_key(struct cache_set *, struct bkey_s_c, s64, bool,
250                        struct bucket_stats_cache_set *);
251 void bch_gc_mark_key(struct cache_set *, struct bkey_s_c, s64, bool);
252 void bch_mark_key(struct cache_set *, struct bkey_s_c, s64, bool,
253                   struct gc_pos, struct bucket_stats_cache_set *, u64);
254
255 void bch_recalc_sectors_available(struct cache_set *);
256
257 void bch_disk_reservation_put(struct cache_set *,
258                               struct disk_reservation *);
259
260 #define BCH_DISK_RESERVATION_NOFAIL             (1 << 0)
261 #define BCH_DISK_RESERVATION_METADATA           (1 << 1)
262 #define BCH_DISK_RESERVATION_GC_LOCK_HELD       (1 << 2)
263 #define BCH_DISK_RESERVATION_BTREE_LOCKS_HELD   (1 << 3)
264
265 int bch_disk_reservation_add(struct cache_set *,
266                              struct disk_reservation *,
267                              unsigned, int);
268 int bch_disk_reservation_get(struct cache_set *,
269                              struct disk_reservation *,
270                              unsigned, int);
271
272 #endif /* _BUCKETS_H */