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