| 1 | /* @title: Anonymous Virtual Memory Area */ |
| 2 | #pragma once |
| 3 | #include <compiler/wrapper.h> |
| 4 | #include <err.h> |
| 5 | #include <structures/rbit.h> |
| 6 | #include <sync/rwlock.h> |
| 7 | #include <types/refcount.h> |
| 8 | #include <types/types.h> |
| 9 | |
| 10 | /* LOCK ORDERING: |
| 11 | * |
| 12 | * mmap_lock |
| 13 | * └─ anon_vma->root->lock |
| 14 | * └─ folio_lock |
| 15 | */ |
| 16 | |
| 17 | struct vma_range; |
| 18 | |
| 19 | struct anon_vma { |
| 20 | struct rbit tree; /* AVCs, keyed in OBJECT-SPACE [pgoff, pgoff+npages) */ |
| 21 | struct anon_vma *root; /* oldest ancestor, the lock is here */ |
| 22 | struct anon_vma *parent; /* immediate ancestor in fork hierarchy */ |
| 23 | struct rwlock lock; /* acquire via root */ |
| 24 | refcount_t refcount; /* # AVCs + folios referencing this object */ |
| 25 | }; |
| 26 | |
| 27 | struct anon_vma *anon_vma_alloc(void) cw_alloc(); |
| 28 | void anon_vma_free(struct anon_vma *av); /* refcount==0 only */ |
| 29 | enum err anon_vma_fork(struct vma_range *child, struct vma_range *parent); |
| 30 | |
| 31 | /* root->lock */ |
| 32 | static inline void anon_vma_read_lock(struct anon_vma *av) |
| 33 | TSA_ACQUIRES(&av->root->lock) { |
| 34 | rw_read_lock(&av->root->lock); |
| 35 | } |
| 36 | |
| 37 | static inline void anon_vma_unlock(struct anon_vma *av) |
| 38 | TSA_RELEASES(&av->root->lock) { |
| 39 | rw_unlock(&av->root->lock); |
| 40 | } |
| 41 | |
| 42 | static inline void anon_vma_write_lock(struct anon_vma *av) |
| 43 | TSA_ACQUIRES(&av->root->lock) { |
| 44 | rw_write_lock(&av->root->lock); |
| 45 | } |
| 46 | |
| 47 | enum err anon_vma_clone(struct vma_range *dst, struct vma_range *src); |
| 48 | |
| 49 | struct anon_vma_chain *anon_vma_itree_first(struct anon_vma *av, pgoff_t first, |
| 50 | pgoff_t last); |
| 51 | struct anon_vma_chain *anon_vma_itree_next(struct anon_vma_chain *avc, |
| 52 | pgoff_t first, pgoff_t last); |
| 53 | |
| 54 | /* teardown: unlink all AVCs on this vma_range, drop anon_vma refs */ |
| 55 | void vma_range_unlink_anon_vmas(struct vma_range *vma_range); |
| 56 | |
| 57 | static inline bool anon_vma_is_root(const struct anon_vma *av) { |
| 58 | return av->root == av; |
| 59 | } |
| 60 | |
| 61 | static inline bool anon_vma_getref(struct anon_vma *av) { |
| 62 | return refcount_inc_not_zero(rc: &av->refcount); |
| 63 | } |
| 64 | |
| 65 | /* Drop ref: When forked anon_vma dies it releases its root pin, allowing |
| 66 | * the putref to go up the hierarchy, but it's bounded, as root is its own |
| 67 | * root */ |
| 68 | static inline void anon_vma_putref(struct anon_vma *av) { |
| 69 | while (av) { |
| 70 | struct anon_vma *root = av->root; |
| 71 | if (!refcount_dec_and_test(rc: &av->refcount)) |
| 72 | return; |
| 73 | anon_vma_free(av); |
| 74 | av = (root != av) ? root : NULL; |
| 75 | } |
| 76 | } |
| 77 | |