1 //----------------------------------------------------------------------
2 // This software is part of the OpenBeOS distribution and is covered
3 // by the OpenBeOS license.
5 // Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
6 //----------------------------------------------------------------------
8 /*! \file Allocator.cpp
10 Physical block allocator class implementation.
13 #include "Allocator.h"
19 /*! \brief Creates a new Allocator object.
21 Allocator::Allocator(uint32 blockSize
)
23 , fBlockSize(blockSize
)
25 , fInitStatus(B_NO_INIT
)
27 status_t error
= get_block_shift(BlockSize(), fBlockShift
);
33 Allocator::InitCheck() const
38 /*! \brief Allocates the given block, if available.
42 - error code: Failure, the block has already been allocated.
45 Allocator::GetBlock(uint32 block
)
47 extent_address
extent(block
, BlockSize());
48 return GetExtent(extent
);
51 /*! \brief Allocates the given extent, if available.
55 - error code: Failure, the extent (or some portion of it) has already
59 Allocator::GetExtent(extent_address extent
)
61 status_t error
= InitCheck();
63 uint32 offset
= extent
.location();
64 uint32 length
= BlocksFor(extent
.length());
65 // First see if the extent is past the allocation tail,
66 // since we then don't have to do any chunklist traversal
67 if (offset
>= Length()) {
68 // Add a new chunk to the end of the chunk list if
70 if (offset
> Length()) {
71 extent_address
chunk(Length(), (offset
-Length())<<BlockShift());
72 fChunkList
.push_back(chunk
);
75 fLength
= offset
+length
;
78 // Block is not past tail, so check the chunk list
79 for (list
<extent_address
>::iterator i
= fChunkList
.begin();
80 i
!= fChunkList
.end();
83 uint32 chunkOffset
= i
->location();
84 uint32 chunkLength
= BlocksFor(i
->length());
85 if (chunkOffset
<= offset
&& (offset
+length
) <= (chunkOffset
+chunkLength
)) {
86 // Found it. Split the chunk. First look for an orphan
87 // before the block, then after.
88 if (chunkOffset
< offset
) {
89 // Orhpan before; add a new chunk in front
91 extent_address
chunk(chunkOffset
, (offset
-chunkOffset
)<<BlockShift());
92 fChunkList
.insert(i
, chunk
);
94 if ((offset
+length
) < (chunkOffset
+chunkLength
)) {
95 // Orphan after; resize the original chunk
96 i
->set_location(offset
+length
);
97 i
->set_length(((chunkOffset
+chunkLength
)-(offset
+length
))<<BlockSize());
99 // No orphan after; remove the original chunk
105 // No matching chunk found, we're SOL.
112 /*! \brief Allocates the next available block.
114 \param block Output parameter into which the number of the
115 allocated block is stored.
116 \param minimumBlock The minimum acceptable block number (used
117 by the physical partition allocator).
121 - error code: Failure, no blocks available.
124 Allocator::GetNextBlock(uint32
&block
, uint32 minimumBlock
)
126 status_t error
= InitCheck();
128 extent_address extent
;
129 error
= GetNextExtent(BlockSize(), true, extent
, minimumBlock
);
131 block
= extent
.location();
136 /*! \brief Allocates the next available extent of given length.
138 \param length The desired length (in bytes) of the extent.
139 \param contiguous If false, signals that an extent of shorter length will
140 be accepted. This allows for small chunks of
141 unallocated space to be consumed, provided a
142 contiguous chunk is not needed.
143 \param extent Output parameter into which the extent as allocated
144 is stored. Note that the length field of the extent
145 may be shorter than the length parameter passed
146 to this function is \a contiguous is false.
147 \param minimumStartingBlock The minimum acceptable starting block
148 for the extent (used by the physical
149 partition allocator).
153 - error code: Failure.
156 Allocator::GetNextExtent(uint32 _length
, bool contiguous
,
157 extent_address
&extent
,
158 uint32 minimumStartingBlock
)
160 DEBUG_INIT_ETC("Allocator", ("length: %lld, contiguous: %d", _length
, contiguous
));
161 uint32 length
= BlocksFor(_length
);
162 bool isPartial
= false;
163 status_t error
= InitCheck();
164 PRINT(("allocation length: %lu\n", Length()));
166 for (list
<extent_address
>::iterator i
= fChunkList
.begin();
167 i
!= fChunkList
.end();
170 uint32 chunkOffset
= i
->location();
171 uint32 chunkLength
= BlocksFor(i
->length());
172 if (chunkOffset
< minimumStartingBlock
)
174 if (minimumStartingBlock
< chunkOffset
+chunkLength
) {
175 // Start of chunk is below min starting block. See if
176 // any part of the chunk would make for an acceptable
178 uint32 difference
= minimumStartingBlock
- chunkOffset
;
179 uint32 newOffset
= minimumStartingBlock
;
180 uint32 newLength
= chunkLength
-difference
;
181 if (length
<= newLength
) {
182 // new chunk is still long enough
183 extent_address
newExtent(newOffset
, _length
);
184 if (GetExtent(newExtent
) == B_OK
) {
188 } else if (!contiguous
) {
189 // new chunk is too short, but we're allowed to
190 // allocate a shorter extent, so we'll do it.
191 extent_address
newExtent(newOffset
, newLength
<<BlockShift());
192 if (GetExtent(newExtent
) == B_OK
) {
198 } else if (length
<= chunkLength
) {
199 // Chunk is larger than necessary. Allocate first
200 // length blocks, and resize the chunk appropriately.
201 extent
.set_location(chunkOffset
);
202 extent
.set_length(_length
);
203 if (length
!= chunkLength
) {
204 i
->set_location(chunkOffset
+length
);
205 i
->set_length((chunkLength
-length
)<<BlockShift());
210 } else if (!contiguous
) {
211 extent
.set_location(chunkOffset
);
212 extent
.set_length(chunkLength
<<BlockShift());
217 // No sufficient chunk found, so try to allocate from the tail
218 PRINT(("ULONG_MAX: %lu\n", ULONG_MAX
));
219 uint32 maxLength
= ULONG_MAX
-Length();
220 PRINT(("maxLength: %lu\n", maxLength
));
221 error
= maxLength
> 0 ? B_OK
: B_DEVICE_FULL
;
223 if (minimumStartingBlock
> Tail())
224 maxLength
-= minimumStartingBlock
- Tail();
225 uint32 tail
= minimumStartingBlock
> Tail() ? minimumStartingBlock
: Tail();
226 if (length
> maxLength
) {
228 error
= B_DEVICE_FULL
;
235 extent_address
newExtent(tail
, isPartial
? length
<<BlockShift() : _length
);
236 if (GetExtent(newExtent
) == B_OK
) {
246 /*! \brief Returns the number of blocks needed to accomodate the
247 given number of bytes.
250 Allocator::BlocksFor(off_t bytes
)
252 if (BlockSize() == 0) {
253 DEBUG_INIT_ETC("Allocator", ("bytes: %ld\n", bytes
));
254 PRINT(("WARNING: Allocator::BlockSize() == 0!\n"));
257 off_t blocks
= bytes
>> BlockShift();
258 if (bytes
% BlockSize() != 0)
260 uint64 mask
= 0xffffffff;
263 // ToDo: Convert this to actually signal an error
264 DEBUG_INIT_ETC("Allocator", ("bytes: %ld\n", bytes
));
265 PRINT(("WARNING: bytes argument too large for corresponding number "
266 "of blocks to be specified with a uint32! (bytes: %Ld, blocks: %Ld, "
267 "maxblocks: %ld).\n", bytes
, blocks
, ULONG_MAX
));