1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#pragma once 6 7#include <stdbool.h> 8#include <stdio.h> 9#include <stdlib.h> 10 11#include <ddk/device.h> 12#include <fvm/fvm.h> 13#include <zircon/device/block.h> 14#include <zircon/thread_annotations.h> 15#include <zircon/types.h> 16 17#ifdef __cplusplus 18 19#include <ddktl/device.h> 20#include <ddktl/protocol/block.h> 21#include <lib/fzl/mapped-vmo.h> 22#include <fbl/algorithm.h> 23#include <fbl/intrusive_wavl_tree.h> 24#include <fbl/mutex.h> 25#include <fbl/unique_ptr.h> 26#include <fbl/vector.h> 27 28namespace fvm { 29 30class SliceExtent : public fbl::WAVLTreeContainable<fbl::unique_ptr<SliceExtent>> { 31public: 32 size_t GetKey() const { return vslice_start_; } 33 // Vslice start (inclusive) 34 size_t start() const { return vslice_start_; } 35 // Vslice end (exclusive) 36 size_t end() const { return vslice_start_ + pslices_.size(); } 37 // Extent length 38 size_t size() const { return end() - start(); } 39 // Look up a pslice given a vslice 40 uint32_t get(size_t vslice) const { 41 if (vslice - vslice_start_ >= pslices_.size()) { 42 return 0; 43 } 44 return pslices_[vslice - vslice_start_]; 45 } 46 47 // Breaks the extent from: 48 // [start(), end()) 49 // Into: 50 // [start(), vslice] and [vslice + 1, end()). 51 // Returns the latter extent on success; returns nullptr 52 // if a memory allocation failure occurs. 53 fbl::unique_ptr<SliceExtent> Split(size_t vslice); 54 55 // Combines the other extent into this one. 56 // 'other' must immediately follow the current slice. 57 bool Merge(const SliceExtent& other); 58 59 bool push_back(uint32_t pslice) { 60 ZX_DEBUG_ASSERT(pslice != PSLICE_UNALLOCATED); 61 fbl::AllocChecker ac; 62 pslices_.push_back(pslice, &ac); 63 return ac.check(); 64 } 65 void pop_back() { pslices_.pop_back(); } 66 bool is_empty() const { return pslices_.size() == 0; } 67 68 SliceExtent(size_t vslice_start) 69 : vslice_start_(vslice_start) {} 70 71private: 72 friend class TypeWAVLTraits; 73 DISALLOW_COPY_ASSIGN_AND_MOVE(SliceExtent); 74 75 fbl::Vector<uint32_t> pslices_; 76 const size_t vslice_start_; 77}; 78 79class VPartitionManager; 80using ManagerDeviceType = ddk::Device<VPartitionManager, ddk::Ioctlable, ddk::Unbindable>; 81 82class VPartition; 83using PartitionDeviceType = ddk::Device<VPartition, 84 ddk::Ioctlable, 85 ddk::GetSizable, 86 ddk::Unbindable>; 87 88class VPartitionManager : public ManagerDeviceType { 89public: 90 DISALLOW_COPY_ASSIGN_AND_MOVE(VPartitionManager); 91 static zx_status_t Bind(zx_device_t* dev); 92 93 // Read the underlying block device, initialize the recorded VPartitions. 94 zx_status_t Load(); 95 96 // Block Protocol 97 size_t BlockOpSize() const { return block_op_size_; } 98 void Queue(block_op_t* txn) const { bp_.ops->queue(bp_.ctx, txn); } 99 100 // Queue a BLOCK_OP_FLUSH request and wait for it to complete 101 zx_status_t Flush() const; 102 103 // Acquire access to a VPart Entry which has already been modified (and 104 // will, as a consequence, not be de-allocated underneath us). 105 vpart_entry_t* GetAllocatedVPartEntry(size_t index) const TA_NO_THREAD_SAFETY_ANALYSIS { 106 auto entry = GetVPartEntryLocked(index); 107 ZX_DEBUG_ASSERT(entry->slices > 0); 108 return entry; 109 } 110 111 // Allocate 'count' slices, write back the FVM. 112 zx_status_t AllocateSlices(VPartition* vp, size_t vslice_start, size_t count) TA_EXCL(lock_); 113 114 // Deallocate 'count' slices, write back the FVM. 115 // If a request is made to remove vslice_count = 0, deallocates the entire 116 // VPartition. 117 zx_status_t FreeSlices(VPartition* vp, size_t vslice_start, size_t count) TA_EXCL(lock_); 118 119 // Returns global information about the FVM. 120 void Query(fvm_info_t* info) TA_EXCL(lock_); 121 122 size_t DiskSize() const { return info_.block_count * info_.block_size; } 123 size_t SliceSize() const { return slice_size_; } 124 size_t VSliceMax() const { return VSLICE_MAX; } 125 const block_info_t& Info() const { return info_; } 126 127 zx_status_t DdkIoctl(uint32_t op, const void* cmd, size_t cmdlen, 128 void* reply, size_t max, size_t* out_actual); 129 void DdkUnbind(); 130 void DdkRelease(); 131 132 VPartitionManager(zx_device_t* dev, const block_info_t& info, size_t block_op_size, 133 const block_protocol_t* bp); 134 ~VPartitionManager(); 135 136private: 137 // Marks the partition with instance GUID |old_guid| as inactive, 138 // and marks partitions with instance GUID |new_guid| as active. 139 // 140 // If a partition with |old_guid| does not exist, it is ignored. 141 // If |old_guid| equals |new_guid|, then |old_guid| is ignored. 142 // If a partition with |new_guid| does not exist, |ZX_ERR_NOT_FOUND| 143 // is returned. 144 // 145 // Updates the FVM metadata atomically. 146 zx_status_t Upgrade(const uint8_t* old_guid, const uint8_t* new_guid) TA_EXCL(lock_); 147 148 // Given a VPartition object, add a corresponding ddk device. 149 zx_status_t AddPartition(fbl::unique_ptr<VPartition> vp) const; 150 151 // Update, hash, and write back the current copy of the FVM metadata. 152 // Automatically handles alternating writes to primary / backup copy of FVM. 153 zx_status_t WriteFvmLocked() TA_REQ(lock_); 154 155 zx_status_t AllocateSlicesLocked(VPartition* vp, size_t vslice_start, 156 size_t count) TA_REQ(lock_); 157 158 zx_status_t FreeSlicesLocked(VPartition* vp, size_t vslice_start, 159 size_t count) TA_REQ(lock_); 160 161 zx_status_t FindFreeVPartEntryLocked(size_t* out) const TA_REQ(lock_); 162 zx_status_t FindFreeSliceLocked(size_t* out, size_t hint) const TA_REQ(lock_); 163 164 fvm_t* GetFvmLocked() const TA_REQ(lock_) { 165 return reinterpret_cast<fvm_t*>(metadata_->GetData()); 166 } 167 168 // Mark a slice as free in the metadata structure. 169 // Update free slice accounting. 170 void FreePhysicalSlice(size_t pslice) TA_REQ(lock_); 171 172 // Mark a slice as allocated in the metadata structure. 173 // Update allocated slice accounting. 174 void AllocatePhysicalSlice(size_t pslice, uint64_t vpart, uint64_t vslice) TA_REQ(lock_); 175 176 // Given a physical slice (acting as an index into the slice table), 177 // return the associated slice entry. 178 slice_entry_t* GetSliceEntryLocked(size_t index) const TA_REQ(lock_); 179 180 // Given an index into the vpartition table, return the associated 181 // virtual partition entry. 182 vpart_entry_t* GetVPartEntryLocked(size_t index) const TA_REQ(lock_); 183 184 size_t PrimaryOffsetLocked() const TA_REQ(lock_) { 185 return first_metadata_is_primary_ ? 0 : MetadataSize(); 186 } 187 188 size_t BackupOffsetLocked() const TA_REQ(lock_) { 189 return first_metadata_is_primary_ ? MetadataSize() : 0; 190 } 191 192 size_t MetadataSize() const { 193 return metadata_size_; 194 } 195 196 zx_status_t DoIoLocked(zx_handle_t vmo, size_t off, size_t len, uint32_t command); 197 198 thrd_t initialization_thread_; 199 block_info_t info_; // Cached info from parent device 200 201 fbl::Mutex lock_; 202 fbl::unique_ptr<fzl::MappedVmo> metadata_ TA_GUARDED(lock_); 203 bool first_metadata_is_primary_ TA_GUARDED(lock_); 204 size_t metadata_size_; 205 size_t slice_size_; 206 // Number of allocatable slices. 207 size_t pslice_total_count_; 208 // Number of currently allocated slices. 209 size_t pslice_allocated_count_ TA_GUARDED(lock_); 210 211 // Block Protocol 212 const size_t block_op_size_; 213 block_protocol_t bp_; 214}; 215 216class VPartition : public PartitionDeviceType, public ddk::BlockProtocol<VPartition> { 217public: 218 static zx_status_t Create(VPartitionManager* vpm, size_t entry_index, 219 fbl::unique_ptr<VPartition>* out); 220 // Device Protocol 221 zx_status_t DdkIoctl(uint32_t op, const void* cmd, size_t cmdlen, 222 void* reply, size_t max, size_t* out_actual); 223 zx_off_t DdkGetSize(); 224 void DdkUnbind(); 225 void DdkRelease(); 226 227 // Block Protocol 228 void BlockQuery(block_info_t* info_out, size_t* block_op_size_out); 229 void BlockQueue(block_op_t* txn); 230 231 auto ExtentBegin() TA_REQ(lock_) { 232 return slice_map_.begin(); 233 } 234 235 // Given a virtual slice, return the physical slice allocated 236 // to it. If no slice is allocated, return PSLICE_UNALLOCATED. 237 uint32_t SliceGetLocked(size_t vslice) const TA_REQ(lock_); 238 239 // Check slices starting from |vslice_start|. 240 // Sets |*count| to the number of contiguous allocated or unallocated slices found. 241 // Sets |*allocated| to true if the vslice range is allocated, and false otherwise. 242 zx_status_t CheckSlices(size_t vslice_start, size_t* count, bool* allocated) TA_EXCL(lock_); 243 244 zx_status_t SliceSetUnsafe(size_t vslice, uint32_t pslice) TA_NO_THREAD_SAFETY_ANALYSIS { 245 return SliceSetLocked(vslice, pslice); 246 } 247 zx_status_t SliceSetLocked(size_t vslice, uint32_t pslice) TA_REQ(lock_); 248 249 bool SliceCanFree(size_t vslice) const TA_REQ(lock_) { 250 auto extent = --slice_map_.upper_bound(vslice); 251 return extent.IsValid() && extent->get(vslice) != PSLICE_UNALLOCATED; 252 } 253 254 // Returns "true" if slice freed successfully, false otherwise. 255 // If freeing from the back of an extent, guaranteed not to fail. 256 bool SliceFreeLocked(size_t vslice) TA_REQ(lock_); 257 258 // Destroy the extent containing the vslice. 259 void ExtentDestroyLocked(size_t vslice) TA_REQ(lock_); 260 261 size_t BlockSize() const TA_NO_THREAD_SAFETY_ANALYSIS { 262 return info_.block_size; 263 } 264 void AddBlocksLocked(ssize_t nblocks) TA_REQ(lock_) { 265 info_.block_count += nblocks; 266 } 267 268 size_t GetEntryIndex() const { return entry_index_; } 269 270 void KillLocked() TA_REQ(lock_) { entry_index_ = 0; } 271 bool IsKilledLocked() TA_REQ(lock_) { return entry_index_ == 0; } 272 273 VPartition(VPartitionManager* vpm, size_t entry_index, size_t block_op_size); 274 ~VPartition(); 275 fbl::Mutex lock_; 276 277private: 278 DISALLOW_COPY_ASSIGN_AND_MOVE(VPartition); 279 280 VPartitionManager* mgr_; 281 size_t entry_index_; 282 283 // Mapping of virtual slice number (index) to physical slice number (value). 284 // Physical slice zero is reserved to mean "unmapped", so a zeroed slice_map 285 // indicates that the vpartition is completely unmapped, and uses no 286 // physical slices. 287 fbl::WAVLTree<size_t, fbl::unique_ptr<SliceExtent>> slice_map_ TA_GUARDED(lock_); 288 block_info_t info_ TA_GUARDED(lock_); 289}; 290 291} // namespace fvm 292 293#endif // ifdef __cplusplus 294 295__BEGIN_CDECLS 296 297/////////////////// C-compatibility definitions (Provided to C from C++) 298 299// Binds FVM driver to a device; loads the VPartition devices asynchronously in 300// a background thread. 301zx_status_t fvm_bind(zx_device_t* dev); 302 303__END_CDECLS 304