blob: 841ac25fd95042f86d134330a13dafd8521521dd [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/ufs/balloc.c
3 *
4 * Copyright (C) 1998
5 * Daniel Pirkl <daniel.pirkl@email.cz>
6 * Charles University, Faculty of Mathematics and Physics
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -08007 *
8 * UFS2 write support Evgeniy Dushistov <dushistov@mail.ru>, 2007
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10
11#include <linux/fs.h>
12#include <linux/ufs_fs.h>
13#include <linux/stat.h>
14#include <linux/time.h>
15#include <linux/string.h>
16#include <linux/quotaops.h>
17#include <linux/buffer_head.h>
Randy Dunlap16f7e0f2006-01-11 12:17:46 -080018#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/bitops.h>
20#include <asm/byteorder.h>
21
22#include "swab.h"
23#include "util.h"
24
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -080025#define INVBLOCK ((u64)-1L)
26
27static u64 ufs_add_fragments(struct inode *, u64, unsigned, unsigned, int *);
28static u64 ufs_alloc_fragments(struct inode *, unsigned, u64, unsigned, int *);
29static u64 ufs_alloccg_block(struct inode *, struct ufs_cg_private_info *, u64, int *);
30static u64 ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, u64, unsigned);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[];
32static void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int);
33
34/*
35 * Free 'count' fragments from fragment number 'fragment'
36 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -080037void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -070038{
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 struct super_block * sb;
40 struct ufs_sb_private_info * uspi;
41 struct ufs_super_block_first * usb1;
42 struct ufs_cg_private_info * ucpi;
43 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -080044 unsigned cgno, bit, end_bit, bbase, blkmap, i;
45 u64 blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47 sb = inode->i_sb;
48 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +030049 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -080051 UFSD("ENTER, fragment %llu, count %u\n",
52 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54 if (ufs_fragnum(fragment) + count > uspi->s_fpg)
55 ufs_error (sb, "ufs_free_fragments", "internal error");
56
57 lock_super(sb);
58
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -080059 cgno = ufs_dtog(uspi, fragment);
60 bit = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 if (cgno >= uspi->s_ncg) {
62 ufs_panic (sb, "ufs_free_fragments", "freeing blocks are outside device");
63 goto failed;
64 }
65
66 ucpi = ufs_load_cylinder (sb, cgno);
67 if (!ucpi)
68 goto failed;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070069 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 if (!ufs_cg_chkmagic(sb, ucg)) {
71 ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno);
72 goto failed;
73 }
74
75 end_bit = bit + count;
76 bbase = ufs_blknum (bit);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070077 blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1);
79 for (i = bit; i < end_bit; i++) {
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070080 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, i))
81 ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, i);
Evgeniy7b4ee732006-01-14 11:42:06 +030082 else
83 ufs_error (sb, "ufs_free_fragments",
84 "bit already cleared for fragment %u", i);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 }
86
87 DQUOT_FREE_BLOCK (inode, count);
88
89
90 fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -070091 uspi->cs_total.cs_nffree += count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070093 blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
95
96 /*
97 * Trying to reassemble free fragments into block
98 */
99 blkno = ufs_fragstoblks (bbase);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700100 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700102 uspi->cs_total.cs_nffree -= uspi->s_fpb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb);
104 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
105 ufs_clusteracct (sb, ucpi, blkno, 1);
106 fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700107 uspi->cs_total.cs_nbfree++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800109 if (uspi->fs_magic != UFS2_MAGIC) {
110 unsigned cylno = ufs_cbtocylno (bbase);
111
112 fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
113 ufs_cbtorpos(bbase)), 1);
114 fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
115 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 }
117
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700118 ubh_mark_buffer_dirty (USPI_UBH(uspi));
119 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700121 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700122 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
124 sb->s_dirt = 1;
125
126 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700127 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 return;
129
130failed:
131 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700132 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 return;
134}
135
136/*
137 * Free 'count' fragments from fragment number 'fragment' (free whole blocks)
138 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800139void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700140{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 struct super_block * sb;
142 struct ufs_sb_private_info * uspi;
143 struct ufs_super_block_first * usb1;
144 struct ufs_cg_private_info * ucpi;
145 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800146 unsigned overflow, cgno, bit, end_bit, i;
147 u64 blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149 sb = inode->i_sb;
150 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300151 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800153 UFSD("ENTER, fragment %llu, count %u\n",
154 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156 if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
157 ufs_error (sb, "ufs_free_blocks", "internal error, "
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800158 "fragment %llu, count %u\n",
159 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 goto failed;
161 }
162
163 lock_super(sb);
164
165do_more:
166 overflow = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800167 cgno = ufs_dtog(uspi, fragment);
168 bit = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 if (cgno >= uspi->s_ncg) {
170 ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700171 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 }
173 end_bit = bit + count;
174 if (end_bit > uspi->s_fpg) {
175 overflow = bit + count - uspi->s_fpg;
176 count -= overflow;
177 end_bit -= overflow;
178 }
179
180 ucpi = ufs_load_cylinder (sb, cgno);
181 if (!ucpi)
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700182 goto failed_unlock;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700183 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 if (!ufs_cg_chkmagic(sb, ucg)) {
185 ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700186 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
188
189 for (i = bit; i < end_bit; i += uspi->s_fpb) {
190 blkno = ufs_fragstoblks(i);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700191 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
193 }
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700194 ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
196 ufs_clusteracct (sb, ucpi, blkno, 1);
197 DQUOT_FREE_BLOCK(inode, uspi->s_fpb);
198
199 fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700200 uspi->cs_total.cs_nbfree++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800202
203 if (uspi->fs_magic != UFS2_MAGIC) {
204 unsigned cylno = ufs_cbtocylno(i);
205
206 fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
207 ufs_cbtorpos(i)), 1);
208 fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
209 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 }
211
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700212 ubh_mark_buffer_dirty (USPI_UBH(uspi));
213 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700215 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700216 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 }
218
219 if (overflow) {
220 fragment += count;
221 count = overflow;
222 goto do_more;
223 }
224
225 sb->s_dirt = 1;
226 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700227 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 return;
229
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700230failed_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 unlock_super (sb);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700232failed:
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700233 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return;
235}
236
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700237/*
238 * Modify inode page cache in such way:
239 * have - blocks with b_blocknr equal to oldb...oldb+count-1
240 * get - blocks with b_blocknr equal to newb...newb+count-1
241 * also we suppose that oldb...oldb+count-1 blocks
242 * situated at the end of file.
243 *
244 * We can come here from ufs_writepage or ufs_prepare_write,
245 * locked_page is argument of these functions, so we already lock it.
246 */
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800247static void ufs_change_blocknr(struct inode *inode, sector_t beg,
248 unsigned int count, sector_t oldb,
249 sector_t newb, struct page *locked_page)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700250{
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800251 const unsigned blks_per_page =
252 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits);
253 const unsigned mask = blks_per_page - 1;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800254 struct address_space * const mapping = inode->i_mapping;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800255 pgoff_t index, cur_index, last_index;
256 unsigned pos, j, lblock;
257 sector_t end, i;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700258 struct page *page;
259 struct buffer_head *head, *bh;
260
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800261 UFSD("ENTER, ino %lu, count %u, oldb %llu, newb %llu\n",
262 inode->i_ino, count,
263 (unsigned long long)oldb, (unsigned long long)newb);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700264
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800265 BUG_ON(!locked_page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700266 BUG_ON(!PageLocked(locked_page));
267
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800268 cur_index = locked_page->index;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800269 end = count + beg;
270 last_index = end >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
271 for (i = beg; i < end; i = (i | mask) + 1) {
272 index = i >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700273
274 if (likely(cur_index != index)) {
275 page = ufs_get_locked_page(mapping, index);
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800276 if (!page)/* it was truncated */
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700277 continue;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800278 if (IS_ERR(page)) {/* or EIO */
279 ufs_error(inode->i_sb, __FUNCTION__,
280 "read of page %llu failed\n",
281 (unsigned long long)index);
282 continue;
283 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700284 } else
285 page = locked_page;
286
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700287 head = page_buffers(page);
288 bh = head;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800289 pos = i & mask;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800290 for (j = 0; j < pos; ++j)
291 bh = bh->b_this_page;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800292
293
294 if (unlikely(index == last_index))
295 lblock = end & mask;
296 else
297 lblock = blks_per_page;
298
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700299 do {
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800300 if (j >= lblock)
301 break;
302 pos = (i - beg) + j;
303
304 if (!buffer_mapped(bh))
305 map_bh(bh, inode->i_sb, oldb + pos);
306 if (!buffer_uptodate(bh)) {
307 ll_rw_block(READ, 1, &bh);
308 wait_on_buffer(bh);
309 if (!buffer_uptodate(bh)) {
310 ufs_error(inode->i_sb, __FUNCTION__,
311 "read of block failed\n");
312 break;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800313 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700314 }
315
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800316 UFSD(" change from %llu to %llu, pos %u\n",
317 (unsigned long long)pos + oldb,
318 (unsigned long long)pos + newb, pos);
319
320 bh->b_blocknr = newb + pos;
321 unmap_underlying_metadata(bh->b_bdev,
322 bh->b_blocknr);
323 mark_buffer_dirty(bh);
324 ++j;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700325 bh = bh->b_this_page;
326 } while (bh != head);
327
Evgeniy Dushistov10e5dce2006-07-01 04:36:24 -0700328 if (likely(cur_index != index))
329 ufs_put_locked_page(page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700330 }
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700331 UFSD("EXIT\n");
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700332}
333
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800334static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
335 int sync)
336{
337 struct buffer_head *bh;
338 sector_t end = beg + n;
339
340 for (; beg < end; ++beg) {
341 bh = sb_getblk(inode->i_sb, beg);
342 lock_buffer(bh);
343 memset(bh->b_data, 0, inode->i_sb->s_blocksize);
344 set_buffer_uptodate(bh);
345 mark_buffer_dirty(bh);
346 unlock_buffer(bh);
347 if (IS_SYNC(inode) || sync)
348 sync_dirty_buffer(bh);
349 brelse(bh);
350 }
351}
352
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800353u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment,
354 u64 goal, unsigned count, int *err,
355 struct page *locked_page)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 struct super_block * sb;
358 struct ufs_sb_private_info * uspi;
359 struct ufs_super_block_first * usb1;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800360 unsigned cgno, oldcount, newcount;
361 u64 tmp, request, result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800363 UFSD("ENTER, ino %lu, fragment %llu, goal %llu, count %u\n",
364 inode->i_ino, (unsigned long long)fragment,
365 (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 sb = inode->i_sb;
368 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300369 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 *err = -ENOSPC;
371
372 lock_super (sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800373 tmp = ufs_data_ptr_to_cpu(sb, p);
374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800376 ufs_warning(sb, "ufs_new_fragments", "internal warning"
377 " fragment %llu, count %u",
378 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 count = uspi->s_fpb - ufs_fragnum(fragment);
380 }
381 oldcount = ufs_fragnum (fragment);
382 newcount = oldcount + count;
383
384 /*
385 * Somebody else has just allocated our fragments
386 */
387 if (oldcount) {
388 if (!tmp) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800389 ufs_error(sb, "ufs_new_fragments", "internal error, "
390 "fragment %llu, tmp %llu\n",
391 (unsigned long long)fragment,
392 (unsigned long long)tmp);
393 unlock_super(sb);
394 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396 if (fragment < UFS_I(inode)->i_lastfrag) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700397 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 unlock_super (sb);
399 return 0;
400 }
401 }
402 else {
403 if (tmp) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700404 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 unlock_super(sb);
406 return 0;
407 }
408 }
409
410 /*
411 * There is not enough space for user on the device
412 */
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700413 if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(uspi, UFS_MINFREE) <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700415 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return 0;
417 }
418
419 if (goal >= uspi->s_size)
420 goal = 0;
421 if (goal == 0)
422 cgno = ufs_inotocg (inode->i_ino);
423 else
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800424 cgno = ufs_dtog(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
426 /*
427 * allocate new fragment
428 */
429 if (oldcount == 0) {
430 result = ufs_alloc_fragments (inode, cgno, goal, count, err);
431 if (result) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800432 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 *err = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800434 UFS_I(inode)->i_lastfrag =
435 max_t(u32, UFS_I(inode)->i_lastfrag,
436 fragment + count);
437 ufs_clear_frags(inode, result + oldcount,
438 newcount - oldcount, locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800441 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 return result;
443 }
444
445 /*
446 * resize block
447 */
448 result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
449 if (result) {
450 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800452 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
453 locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800455 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 return result;
457 }
458
459 /*
460 * allocate new block and move data
461 */
462 switch (fs32_to_cpu(sb, usb1->fs_optim)) {
463 case UFS_OPTSPACE:
464 request = newcount;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700465 if (uspi->s_minfree < 5 || uspi->cs_total.cs_nffree
466 > uspi->s_dsize * uspi->s_minfree / (2 * 100))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 break;
468 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
469 break;
470 default:
471 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
472
473 case UFS_OPTTIME:
474 request = uspi->s_fpb;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700475 if (uspi->cs_total.cs_nffree < uspi->s_dsize *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 (uspi->s_minfree - 2) / 100)
477 break;
478 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
479 break;
480 }
481 result = ufs_alloc_fragments (inode, cgno, goal, request, err);
482 if (result) {
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800483 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
484 locked_page != NULL);
Evgeniy Dushistov4b25a372007-03-16 13:38:09 -0800485 ufs_change_blocknr(inode, fragment - oldcount, oldcount,
486 uspi->s_sbbase + tmp,
487 uspi->s_sbbase + result, locked_page);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800488 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 unlock_super(sb);
492 if (newcount < request)
493 ufs_free_fragments (inode, result + newcount, request - newcount);
494 ufs_free_fragments (inode, tmp, oldcount);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800495 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 return result;
497 }
498
499 unlock_super(sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700500 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 return 0;
502}
503
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800504static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
505 unsigned oldcount, unsigned newcount, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506{
507 struct super_block * sb;
508 struct ufs_sb_private_info * uspi;
509 struct ufs_super_block_first * usb1;
510 struct ufs_cg_private_info * ucpi;
511 struct ufs_cylinder_group * ucg;
512 unsigned cgno, fragno, fragoff, count, fragsize, i;
513
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800514 UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
515 (unsigned long long)fragment, oldcount, newcount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
517 sb = inode->i_sb;
518 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300519 usb1 = ubh_get_usb_first (uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 count = newcount - oldcount;
521
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800522 cgno = ufs_dtog(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 if (fs32_to_cpu(sb, UFS_SB(sb)->fs_cs(cgno).cs_nffree) < count)
524 return 0;
525 if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
526 return 0;
527 ucpi = ufs_load_cylinder (sb, cgno);
528 if (!ucpi)
529 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700530 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 if (!ufs_cg_chkmagic(sb, ucg)) {
532 ufs_panic (sb, "ufs_add_fragments",
533 "internal error, bad magic number on cg %u", cgno);
534 return 0;
535 }
536
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800537 fragno = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 fragoff = ufs_fragnum (fragno);
539 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700540 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 return 0;
542 /*
543 * Block can be extended
544 */
545 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
546 for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700547 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 break;
549 fragsize = i - oldcount;
550 if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
551 ufs_panic (sb, "ufs_add_fragments",
552 "internal error or corrupted bitmap on cg %u", cgno);
553 fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
554 if (fragsize != count)
555 fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
556 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700557 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 if(DQUOT_ALLOC_BLOCK(inode, count)) {
559 *err = -EDQUOT;
560 return 0;
561 }
562
563 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
564 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700565 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700567 ubh_mark_buffer_dirty (USPI_UBH(uspi));
568 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700570 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700571 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573 sb->s_dirt = 1;
574
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800575 UFSD("EXIT, fragment %llu\n", (unsigned long long)fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
577 return fragment;
578}
579
580#define UFS_TEST_FREE_SPACE_CG \
581 ucg = (struct ufs_cylinder_group *) UFS_SB(sb)->s_ucg[cgno]->b_data; \
582 if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
583 goto cg_found; \
584 for (k = count; k < uspi->s_fpb; k++) \
585 if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
586 goto cg_found;
587
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800588static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
589 u64 goal, unsigned count, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590{
591 struct super_block * sb;
592 struct ufs_sb_private_info * uspi;
593 struct ufs_super_block_first * usb1;
594 struct ufs_cg_private_info * ucpi;
595 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800596 unsigned oldcg, i, j, k, allocsize;
597 u64 result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800599 UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
600 inode->i_ino, cgno, (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 sb = inode->i_sb;
603 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300604 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 oldcg = cgno;
606
607 /*
608 * 1. searching on preferred cylinder group
609 */
610 UFS_TEST_FREE_SPACE_CG
611
612 /*
613 * 2. quadratic rehash
614 */
615 for (j = 1; j < uspi->s_ncg; j *= 2) {
616 cgno += j;
617 if (cgno >= uspi->s_ncg)
618 cgno -= uspi->s_ncg;
619 UFS_TEST_FREE_SPACE_CG
620 }
621
622 /*
623 * 3. brute force search
624 * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
625 */
626 cgno = (oldcg + 1) % uspi->s_ncg;
627 for (j = 2; j < uspi->s_ncg; j++) {
628 cgno++;
629 if (cgno >= uspi->s_ncg)
630 cgno = 0;
631 UFS_TEST_FREE_SPACE_CG
632 }
633
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700634 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 return 0;
636
637cg_found:
638 ucpi = ufs_load_cylinder (sb, cgno);
639 if (!ucpi)
640 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700641 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 if (!ufs_cg_chkmagic(sb, ucg))
643 ufs_panic (sb, "ufs_alloc_fragments",
644 "internal error, bad magic number on cg %u", cgno);
645 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
646
647 if (count == uspi->s_fpb) {
648 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800649 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 return 0;
651 goto succed;
652 }
653
654 for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
655 if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
656 break;
657
658 if (allocsize == uspi->s_fpb) {
659 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800660 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 return 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800662 goal = ufs_dtogd(uspi, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 for (i = count; i < uspi->s_fpb; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700664 ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 i = uspi->s_fpb - count;
666 DQUOT_FREE_BLOCK(inode, i);
667
668 fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700669 uspi->cs_total.cs_nffree += i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i);
671 fs32_add(sb, &ucg->cg_frsum[i], 1);
672 goto succed;
673 }
674
675 result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800676 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return 0;
678 if(DQUOT_ALLOC_BLOCK(inode, count)) {
679 *err = -EDQUOT;
680 return 0;
681 }
682 for (i = 0; i < count; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700683 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
685 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700686 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
688 fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
689
690 if (count != allocsize)
691 fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
692
693succed:
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700694 ubh_mark_buffer_dirty (USPI_UBH(uspi));
695 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700697 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700698 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 }
700 sb->s_dirt = 1;
701
702 result += cgno * uspi->s_fpg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800703 UFSD("EXIT3, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 return result;
705}
706
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800707static u64 ufs_alloccg_block(struct inode *inode,
708 struct ufs_cg_private_info *ucpi,
709 u64 goal, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710{
711 struct super_block * sb;
712 struct ufs_sb_private_info * uspi;
713 struct ufs_super_block_first * usb1;
714 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800715 u64 result, blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800717 UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
719 sb = inode->i_sb;
720 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300721 usb1 = ubh_get_usb_first(uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700722 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 if (goal == 0) {
725 goal = ucpi->c_rotor;
726 goto norot;
727 }
728 goal = ufs_blknum (goal);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800729 goal = ufs_dtogd(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
731 /*
732 * If the requested block is available, use it.
733 */
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700734 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 result = goal;
736 goto gotit;
737 }
738
739norot:
740 result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800741 if (result == INVBLOCK)
742 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 ucpi->c_rotor = result;
744gotit:
745 blkno = ufs_fragstoblks(result);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700746 ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
748 ufs_clusteracct (sb, ucpi, blkno, -1);
749 if(DQUOT_ALLOC_BLOCK(inode, uspi->s_fpb)) {
750 *err = -EDQUOT;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800751 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
753
754 fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700755 uspi->cs_total.cs_nbfree--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800757
758 if (uspi->fs_magic != UFS2_MAGIC) {
759 unsigned cylno = ufs_cbtocylno((unsigned)result);
760
761 fs16_sub(sb, &ubh_cg_blks(ucpi, cylno,
762 ufs_cbtorpos((unsigned)result)), 1);
763 fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1);
764 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800766 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
768 return result;
769}
770
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700771static unsigned ubh_scanc(struct ufs_sb_private_info *uspi,
772 struct ufs_buffer_head *ubh,
773 unsigned begin, unsigned size,
774 unsigned char *table, unsigned char mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700776 unsigned rest, offset;
777 unsigned char *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700780 offset = begin & ~uspi->s_fmask;
781 begin >>= uspi->s_fshift;
782 for (;;) {
783 if ((offset + size) < uspi->s_fsize)
784 rest = size;
785 else
786 rest = uspi->s_fsize - offset;
787 size -= rest;
788 cp = ubh->bh[begin]->b_data + offset;
789 while ((table[*cp++] & mask) == 0 && --rest)
790 ;
791 if (rest || !size)
792 break;
793 begin++;
794 offset = 0;
795 }
796 return (size + rest);
797}
798
799/*
800 * Find a block of the specified size in the specified cylinder group.
801 * @sp: pointer to super block
802 * @ucpi: pointer to cylinder group info
803 * @goal: near which block we want find new one
804 * @count: specified size
805 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800806static u64 ufs_bitmap_search(struct super_block *sb,
807 struct ufs_cg_private_info *ucpi,
808 u64 goal, unsigned count)
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700809{
810 /*
811 * Bit patterns for identifying fragments in the block map
812 * used as ((map & mask_arr) == want_arr)
813 */
814 static const int mask_arr[9] = {
815 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
816 };
817 static const int want_arr[9] = {
818 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
819 };
820 struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
821 struct ufs_super_block_first *usb1;
822 struct ufs_cylinder_group *ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800823 unsigned start, length, loc;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700824 unsigned pos, want, blockmap, mask, end;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800825 u64 result;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700826
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800827 UFSD("ENTER, cg %u, goal %llu, count %u\n", ucpi->c_cgx,
828 (unsigned long long)goal, count);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700829
Evgeniy7b4ee732006-01-14 11:42:06 +0300830 usb1 = ubh_get_usb_first (uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700831 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
833 if (goal)
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800834 start = ufs_dtogd(uspi, goal) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 else
836 start = ucpi->c_frotor >> 3;
837
838 length = ((uspi->s_fpg + 7) >> 3) - start;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700839 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
841 1 << (count - 1 + (uspi->s_fpb & 7)));
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700842 if (loc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 length = start + 1;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700844 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length,
845 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb :
846 ufs_fragtable_other,
847 1 << (count - 1 + (uspi->s_fpb & 7)));
848 if (loc == 0) {
849 ufs_error(sb, "ufs_bitmap_search",
850 "bitmap corrupted on cg %u, start %u,"
851 " length %u, count %u, freeoff %u\n",
852 ucpi->c_cgx, start, length, count,
853 ucpi->c_freeoff);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800854 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 }
856 start = 0;
857 }
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700858 result = (start + length - loc) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 ucpi->c_frotor = result;
860
861 /*
862 * found the byte in the map
863 */
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700864
865 for (end = result + 8; result < end; result += uspi->s_fpb) {
866 blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result);
867 blockmap <<= 1;
868 mask = mask_arr[count];
869 want = want_arr[count];
870 for (pos = 0; pos <= uspi->s_fpb - count; pos++) {
871 if ((blockmap & mask) == want) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800872 UFSD("EXIT, result %llu\n",
873 (unsigned long long)result);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700874 return result + pos;
875 }
876 mask <<= 1;
877 want <<= 1;
878 }
879 }
880
881 ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n",
882 ucpi->c_cgx);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700883 UFSD("EXIT (FAILED)\n");
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800884 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885}
886
887static void ufs_clusteracct(struct super_block * sb,
888 struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
889{
890 struct ufs_sb_private_info * uspi;
891 int i, start, end, forw, back;
892
893 uspi = UFS_SB(sb)->s_uspi;
894 if (uspi->s_contigsumsize <= 0)
895 return;
896
897 if (cnt > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700898 ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 else
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700900 ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
902 /*
903 * Find the size of the cluster going forward.
904 */
905 start = blkno + 1;
906 end = start + uspi->s_contigsumsize;
907 if ( end >= ucpi->c_nclusterblks)
908 end = ucpi->c_nclusterblks;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700909 i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 if (i > end)
911 i = end;
912 forw = i - start;
913
914 /*
915 * Find the size of the cluster going backward.
916 */
917 start = blkno - 1;
918 end = start - uspi->s_contigsumsize;
919 if (end < 0 )
920 end = -1;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700921 i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 if ( i < end)
923 i = end;
924 back = start - i;
925
926 /*
927 * Account for old cluster and the possibly new forward and
928 * back clusters.
929 */
930 i = back + forw + 1;
931 if (i > uspi->s_contigsumsize)
932 i = uspi->s_contigsumsize;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700933 fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (back > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700935 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 if (forw > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700937 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938}
939
940
941static unsigned char ufs_fragtable_8fpb[] = {
942 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
943 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
944 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
945 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
946 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
947 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
948 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
949 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
950 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
951 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
952 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
953 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
954 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
955 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
956 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
957 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
958};
959
960static unsigned char ufs_fragtable_other[] = {
961 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
962 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
963 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
964 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
965 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
966 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
967 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
968 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
969 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
970 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
971 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
972 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
973 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
974 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
975 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
976 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
977};