blob: 46f7a807bbc1ec8313af3df1a4c08c2afb3498eb [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <linux/stat.h>
13#include <linux/time.h>
14#include <linux/string.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/buffer_head.h>
Randy Dunlap16f7e0f2006-01-11 12:17:46 -080016#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/bitops.h>
18#include <asm/byteorder.h>
19
Mike Frysingere5420592008-02-08 04:21:31 -080020#include "ufs_fs.h"
Christoph Hellwigbcd6d4e2007-10-16 23:26:51 -070021#include "ufs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#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
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -070088 uspi->cs_total.cs_nffree += count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070090 blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
92
93 /*
94 * Trying to reassemble free fragments into block
95 */
96 blkno = ufs_fragstoblks (bbase);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -070097 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -070099 uspi->cs_total.cs_nffree -= uspi->s_fpb;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb);
101 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
102 ufs_clusteracct (sb, ucpi, blkno, 1);
103 fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700104 uspi->cs_total.cs_nbfree++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800106 if (uspi->fs_magic != UFS2_MAGIC) {
107 unsigned cylno = ufs_cbtocylno (bbase);
108
109 fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
110 ufs_cbtorpos(bbase)), 1);
111 fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 }
114
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700115 ubh_mark_buffer_dirty (USPI_UBH(uspi));
116 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Christoph Hellwig9cb569d2010-08-11 17:06:24 +0200117 if (sb->s_flags & MS_SYNCHRONOUS)
118 ubh_sync_block(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 sb->s_dirt = 1;
120
121 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700122 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 return;
124
125failed:
126 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700127 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 return;
129}
130
131/*
132 * Free 'count' fragments from fragment number 'fragment' (free whole blocks)
133 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800134void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700135{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 struct super_block * sb;
137 struct ufs_sb_private_info * uspi;
138 struct ufs_super_block_first * usb1;
139 struct ufs_cg_private_info * ucpi;
140 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800141 unsigned overflow, cgno, bit, end_bit, i;
142 u64 blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 sb = inode->i_sb;
145 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300146 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800148 UFSD("ENTER, fragment %llu, count %u\n",
149 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151 if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
152 ufs_error (sb, "ufs_free_blocks", "internal error, "
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800153 "fragment %llu, count %u\n",
154 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 goto failed;
156 }
157
158 lock_super(sb);
159
160do_more:
161 overflow = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800162 cgno = ufs_dtog(uspi, fragment);
163 bit = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 if (cgno >= uspi->s_ncg) {
165 ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700166 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 }
168 end_bit = bit + count;
169 if (end_bit > uspi->s_fpg) {
170 overflow = bit + count - uspi->s_fpg;
171 count -= overflow;
172 end_bit -= overflow;
173 }
174
175 ucpi = ufs_load_cylinder (sb, cgno);
176 if (!ucpi)
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700177 goto failed_unlock;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700178 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 if (!ufs_cg_chkmagic(sb, ucg)) {
180 ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700181 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
183
184 for (i = bit; i < end_bit; i += uspi->s_fpb) {
185 blkno = ufs_fragstoblks(i);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700186 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
188 }
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700189 ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
191 ufs_clusteracct (sb, ucpi, blkno, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193 fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700194 uspi->cs_total.cs_nbfree++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800196
197 if (uspi->fs_magic != UFS2_MAGIC) {
198 unsigned cylno = ufs_cbtocylno(i);
199
200 fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
201 ufs_cbtorpos(i)), 1);
202 fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
203 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
205
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700206 ubh_mark_buffer_dirty (USPI_UBH(uspi));
207 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Christoph Hellwig9cb569d2010-08-11 17:06:24 +0200208 if (sb->s_flags & MS_SYNCHRONOUS)
209 ubh_sync_block(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210
211 if (overflow) {
212 fragment += count;
213 count = overflow;
214 goto do_more;
215 }
216
217 sb->s_dirt = 1;
218 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700219 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 return;
221
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700222failed_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 unlock_super (sb);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700224failed:
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700225 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 return;
227}
228
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700229/*
230 * Modify inode page cache in such way:
231 * have - blocks with b_blocknr equal to oldb...oldb+count-1
232 * get - blocks with b_blocknr equal to newb...newb+count-1
233 * also we suppose that oldb...oldb+count-1 blocks
234 * situated at the end of file.
235 *
236 * We can come here from ufs_writepage or ufs_prepare_write,
237 * locked_page is argument of these functions, so we already lock it.
238 */
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800239static void ufs_change_blocknr(struct inode *inode, sector_t beg,
240 unsigned int count, sector_t oldb,
241 sector_t newb, struct page *locked_page)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700242{
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800243 const unsigned blks_per_page =
244 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits);
245 const unsigned mask = blks_per_page - 1;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800246 struct address_space * const mapping = inode->i_mapping;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800247 pgoff_t index, cur_index, last_index;
248 unsigned pos, j, lblock;
249 sector_t end, i;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700250 struct page *page;
251 struct buffer_head *head, *bh;
252
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800253 UFSD("ENTER, ino %lu, count %u, oldb %llu, newb %llu\n",
254 inode->i_ino, count,
255 (unsigned long long)oldb, (unsigned long long)newb);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700256
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800257 BUG_ON(!locked_page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700258 BUG_ON(!PageLocked(locked_page));
259
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800260 cur_index = locked_page->index;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800261 end = count + beg;
262 last_index = end >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
263 for (i = beg; i < end; i = (i | mask) + 1) {
264 index = i >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700265
266 if (likely(cur_index != index)) {
267 page = ufs_get_locked_page(mapping, index);
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800268 if (!page)/* it was truncated */
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700269 continue;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800270 if (IS_ERR(page)) {/* or EIO */
Harvey Harrison9746077a72008-04-28 02:16:17 -0700271 ufs_error(inode->i_sb, __func__,
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800272 "read of page %llu failed\n",
273 (unsigned long long)index);
274 continue;
275 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700276 } else
277 page = locked_page;
278
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700279 head = page_buffers(page);
280 bh = head;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800281 pos = i & mask;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800282 for (j = 0; j < pos; ++j)
283 bh = bh->b_this_page;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800284
285
286 if (unlikely(index == last_index))
287 lblock = end & mask;
288 else
289 lblock = blks_per_page;
290
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700291 do {
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800292 if (j >= lblock)
293 break;
294 pos = (i - beg) + j;
295
296 if (!buffer_mapped(bh))
297 map_bh(bh, inode->i_sb, oldb + pos);
298 if (!buffer_uptodate(bh)) {
299 ll_rw_block(READ, 1, &bh);
300 wait_on_buffer(bh);
301 if (!buffer_uptodate(bh)) {
Harvey Harrison9746077a72008-04-28 02:16:17 -0700302 ufs_error(inode->i_sb, __func__,
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800303 "read of block failed\n");
304 break;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800305 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700306 }
307
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800308 UFSD(" change from %llu to %llu, pos %u\n",
Andrew Morton9df13032008-03-19 17:01:05 -0700309 (unsigned long long)(pos + oldb),
310 (unsigned long long)(pos + newb), pos);
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800311
312 bh->b_blocknr = newb + pos;
313 unmap_underlying_metadata(bh->b_bdev,
314 bh->b_blocknr);
315 mark_buffer_dirty(bh);
316 ++j;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700317 bh = bh->b_this_page;
318 } while (bh != head);
319
Evgeniy Dushistov10e5dce2006-07-01 04:36:24 -0700320 if (likely(cur_index != index))
321 ufs_put_locked_page(page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700322 }
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700323 UFSD("EXIT\n");
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700324}
325
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800326static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
327 int sync)
328{
329 struct buffer_head *bh;
330 sector_t end = beg + n;
331
332 for (; beg < end; ++beg) {
333 bh = sb_getblk(inode->i_sb, beg);
334 lock_buffer(bh);
335 memset(bh->b_data, 0, inode->i_sb->s_blocksize);
336 set_buffer_uptodate(bh);
337 mark_buffer_dirty(bh);
338 unlock_buffer(bh);
339 if (IS_SYNC(inode) || sync)
340 sync_dirty_buffer(bh);
341 brelse(bh);
342 }
343}
344
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800345u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment,
346 u64 goal, unsigned count, int *err,
347 struct page *locked_page)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
349 struct super_block * sb;
350 struct ufs_sb_private_info * uspi;
351 struct ufs_super_block_first * usb1;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800352 unsigned cgno, oldcount, newcount;
353 u64 tmp, request, result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800355 UFSD("ENTER, ino %lu, fragment %llu, goal %llu, count %u\n",
356 inode->i_ino, (unsigned long long)fragment,
357 (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359 sb = inode->i_sb;
360 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300361 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 *err = -ENOSPC;
363
364 lock_super (sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800365 tmp = ufs_data_ptr_to_cpu(sb, p);
366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800368 ufs_warning(sb, "ufs_new_fragments", "internal warning"
369 " fragment %llu, count %u",
370 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 count = uspi->s_fpb - ufs_fragnum(fragment);
372 }
373 oldcount = ufs_fragnum (fragment);
374 newcount = oldcount + count;
375
376 /*
377 * Somebody else has just allocated our fragments
378 */
379 if (oldcount) {
380 if (!tmp) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800381 ufs_error(sb, "ufs_new_fragments", "internal error, "
382 "fragment %llu, tmp %llu\n",
383 (unsigned long long)fragment,
384 (unsigned long long)tmp);
385 unlock_super(sb);
386 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 }
388 if (fragment < UFS_I(inode)->i_lastfrag) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700389 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 unlock_super (sb);
391 return 0;
392 }
393 }
394 else {
395 if (tmp) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700396 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 unlock_super(sb);
398 return 0;
399 }
400 }
401
402 /*
403 * There is not enough space for user on the device
404 */
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700405 if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(uspi, UFS_MINFREE) <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700407 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return 0;
409 }
410
411 if (goal >= uspi->s_size)
412 goal = 0;
413 if (goal == 0)
414 cgno = ufs_inotocg (inode->i_ino);
415 else
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800416 cgno = ufs_dtog(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 /*
419 * allocate new fragment
420 */
421 if (oldcount == 0) {
422 result = ufs_alloc_fragments (inode, cgno, goal, count, err);
423 if (result) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800424 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 *err = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800426 UFS_I(inode)->i_lastfrag =
427 max_t(u32, UFS_I(inode)->i_lastfrag,
428 fragment + count);
429 ufs_clear_frags(inode, result + oldcount,
430 newcount - oldcount, locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 }
432 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800433 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 return result;
435 }
436
437 /*
438 * resize block
439 */
440 result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
441 if (result) {
442 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800444 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
445 locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800447 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 return result;
449 }
450
451 /*
452 * allocate new block and move data
453 */
454 switch (fs32_to_cpu(sb, usb1->fs_optim)) {
455 case UFS_OPTSPACE:
456 request = newcount;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700457 if (uspi->s_minfree < 5 || uspi->cs_total.cs_nffree
458 > uspi->s_dsize * uspi->s_minfree / (2 * 100))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 break;
460 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
461 break;
462 default:
463 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
464
465 case UFS_OPTTIME:
466 request = uspi->s_fpb;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700467 if (uspi->cs_total.cs_nffree < uspi->s_dsize *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 (uspi->s_minfree - 2) / 100)
469 break;
470 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
471 break;
472 }
473 result = ufs_alloc_fragments (inode, cgno, goal, request, err);
474 if (result) {
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800475 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
476 locked_page != NULL);
Evgeniy Dushistov4b25a372007-03-16 13:38:09 -0800477 ufs_change_blocknr(inode, fragment - oldcount, oldcount,
478 uspi->s_sbbase + tmp,
479 uspi->s_sbbase + result, locked_page);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800480 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 unlock_super(sb);
484 if (newcount < request)
485 ufs_free_fragments (inode, result + newcount, request - newcount);
486 ufs_free_fragments (inode, tmp, oldcount);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800487 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 return result;
489 }
490
491 unlock_super(sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700492 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 return 0;
494}
495
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800496static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
497 unsigned oldcount, unsigned newcount, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
499 struct super_block * sb;
500 struct ufs_sb_private_info * uspi;
501 struct ufs_super_block_first * usb1;
502 struct ufs_cg_private_info * ucpi;
503 struct ufs_cylinder_group * ucg;
504 unsigned cgno, fragno, fragoff, count, fragsize, i;
505
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800506 UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
507 (unsigned long long)fragment, oldcount, newcount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 sb = inode->i_sb;
510 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300511 usb1 = ubh_get_usb_first (uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 count = newcount - oldcount;
513
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800514 cgno = ufs_dtog(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 if (fs32_to_cpu(sb, UFS_SB(sb)->fs_cs(cgno).cs_nffree) < count)
516 return 0;
517 if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
518 return 0;
519 ucpi = ufs_load_cylinder (sb, cgno);
520 if (!ucpi)
521 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700522 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 if (!ufs_cg_chkmagic(sb, ucg)) {
524 ufs_panic (sb, "ufs_add_fragments",
525 "internal error, bad magic number on cg %u", cgno);
526 return 0;
527 }
528
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800529 fragno = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 fragoff = ufs_fragnum (fragno);
531 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700532 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 return 0;
534 /*
535 * Block can be extended
536 */
537 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
538 for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700539 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 break;
541 fragsize = i - oldcount;
542 if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
543 ufs_panic (sb, "ufs_add_fragments",
544 "internal error or corrupted bitmap on cg %u", cgno);
545 fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
546 if (fragsize != count)
547 fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
548 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700549 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
551 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
552 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700553 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700555 ubh_mark_buffer_dirty (USPI_UBH(uspi));
556 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Christoph Hellwig9cb569d2010-08-11 17:06:24 +0200557 if (sb->s_flags & MS_SYNCHRONOUS)
558 ubh_sync_block(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 sb->s_dirt = 1;
560
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800561 UFSD("EXIT, fragment %llu\n", (unsigned long long)fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562
563 return fragment;
564}
565
566#define UFS_TEST_FREE_SPACE_CG \
567 ucg = (struct ufs_cylinder_group *) UFS_SB(sb)->s_ucg[cgno]->b_data; \
568 if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
569 goto cg_found; \
570 for (k = count; k < uspi->s_fpb; k++) \
571 if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
572 goto cg_found;
573
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800574static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
575 u64 goal, unsigned count, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
577 struct super_block * sb;
578 struct ufs_sb_private_info * uspi;
579 struct ufs_super_block_first * usb1;
580 struct ufs_cg_private_info * ucpi;
581 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800582 unsigned oldcg, i, j, k, allocsize;
583 u64 result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800585 UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
586 inode->i_ino, cgno, (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588 sb = inode->i_sb;
589 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300590 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 oldcg = cgno;
592
593 /*
594 * 1. searching on preferred cylinder group
595 */
596 UFS_TEST_FREE_SPACE_CG
597
598 /*
599 * 2. quadratic rehash
600 */
601 for (j = 1; j < uspi->s_ncg; j *= 2) {
602 cgno += j;
603 if (cgno >= uspi->s_ncg)
604 cgno -= uspi->s_ncg;
605 UFS_TEST_FREE_SPACE_CG
606 }
607
608 /*
609 * 3. brute force search
610 * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
611 */
612 cgno = (oldcg + 1) % uspi->s_ncg;
613 for (j = 2; j < uspi->s_ncg; j++) {
614 cgno++;
615 if (cgno >= uspi->s_ncg)
616 cgno = 0;
617 UFS_TEST_FREE_SPACE_CG
618 }
619
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700620 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return 0;
622
623cg_found:
624 ucpi = ufs_load_cylinder (sb, cgno);
625 if (!ucpi)
626 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700627 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 if (!ufs_cg_chkmagic(sb, ucg))
629 ufs_panic (sb, "ufs_alloc_fragments",
630 "internal error, bad magic number on cg %u", cgno);
631 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
632
633 if (count == uspi->s_fpb) {
634 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800635 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 return 0;
637 goto succed;
638 }
639
640 for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
641 if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
642 break;
643
644 if (allocsize == uspi->s_fpb) {
645 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800646 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 return 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800648 goal = ufs_dtogd(uspi, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 for (i = count; i < uspi->s_fpb; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700650 ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 i = uspi->s_fpb - count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700654 uspi->cs_total.cs_nffree += i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i);
656 fs32_add(sb, &ucg->cg_frsum[i], 1);
657 goto succed;
658 }
659
660 result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800661 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 for (i = 0; i < count; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700664 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700667 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
669 fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
670
671 if (count != allocsize)
672 fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
673
674succed:
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700675 ubh_mark_buffer_dirty (USPI_UBH(uspi));
676 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Christoph Hellwig9cb569d2010-08-11 17:06:24 +0200677 if (sb->s_flags & MS_SYNCHRONOUS)
678 ubh_sync_block(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 sb->s_dirt = 1;
680
681 result += cgno * uspi->s_fpg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800682 UFSD("EXIT3, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return result;
684}
685
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800686static u64 ufs_alloccg_block(struct inode *inode,
687 struct ufs_cg_private_info *ucpi,
688 u64 goal, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
690 struct super_block * sb;
691 struct ufs_sb_private_info * uspi;
692 struct ufs_super_block_first * usb1;
693 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800694 u64 result, blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800696 UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
698 sb = inode->i_sb;
699 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300700 usb1 = ubh_get_usb_first(uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700701 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
703 if (goal == 0) {
704 goal = ucpi->c_rotor;
705 goto norot;
706 }
707 goal = ufs_blknum (goal);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800708 goal = ufs_dtogd(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 /*
711 * If the requested block is available, use it.
712 */
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700713 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 result = goal;
715 goto gotit;
716 }
717
718norot:
719 result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800720 if (result == INVBLOCK)
721 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 ucpi->c_rotor = result;
723gotit:
724 blkno = ufs_fragstoblks(result);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700725 ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
727 ufs_clusteracct (sb, ucpi, blkno, -1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700730 uspi->cs_total.cs_nbfree--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800732
733 if (uspi->fs_magic != UFS2_MAGIC) {
734 unsigned cylno = ufs_cbtocylno((unsigned)result);
735
736 fs16_sub(sb, &ubh_cg_blks(ucpi, cylno,
737 ufs_cbtorpos((unsigned)result)), 1);
738 fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1);
739 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800741 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
743 return result;
744}
745
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700746static unsigned ubh_scanc(struct ufs_sb_private_info *uspi,
747 struct ufs_buffer_head *ubh,
748 unsigned begin, unsigned size,
749 unsigned char *table, unsigned char mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750{
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700751 unsigned rest, offset;
752 unsigned char *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700755 offset = begin & ~uspi->s_fmask;
756 begin >>= uspi->s_fshift;
757 for (;;) {
758 if ((offset + size) < uspi->s_fsize)
759 rest = size;
760 else
761 rest = uspi->s_fsize - offset;
762 size -= rest;
763 cp = ubh->bh[begin]->b_data + offset;
764 while ((table[*cp++] & mask) == 0 && --rest)
765 ;
766 if (rest || !size)
767 break;
768 begin++;
769 offset = 0;
770 }
771 return (size + rest);
772}
773
774/*
775 * Find a block of the specified size in the specified cylinder group.
776 * @sp: pointer to super block
777 * @ucpi: pointer to cylinder group info
778 * @goal: near which block we want find new one
779 * @count: specified size
780 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800781static u64 ufs_bitmap_search(struct super_block *sb,
782 struct ufs_cg_private_info *ucpi,
783 u64 goal, unsigned count)
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700784{
785 /*
786 * Bit patterns for identifying fragments in the block map
787 * used as ((map & mask_arr) == want_arr)
788 */
789 static const int mask_arr[9] = {
790 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
791 };
792 static const int want_arr[9] = {
793 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
794 };
795 struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
796 struct ufs_super_block_first *usb1;
797 struct ufs_cylinder_group *ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800798 unsigned start, length, loc;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700799 unsigned pos, want, blockmap, mask, end;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800800 u64 result;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700801
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800802 UFSD("ENTER, cg %u, goal %llu, count %u\n", ucpi->c_cgx,
803 (unsigned long long)goal, count);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700804
Evgeniy7b4ee732006-01-14 11:42:06 +0300805 usb1 = ubh_get_usb_first (uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700806 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
808 if (goal)
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800809 start = ufs_dtogd(uspi, goal) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 else
811 start = ucpi->c_frotor >> 3;
812
813 length = ((uspi->s_fpg + 7) >> 3) - start;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700814 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
816 1 << (count - 1 + (uspi->s_fpb & 7)));
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700817 if (loc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 length = start + 1;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700819 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length,
820 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb :
821 ufs_fragtable_other,
822 1 << (count - 1 + (uspi->s_fpb & 7)));
823 if (loc == 0) {
824 ufs_error(sb, "ufs_bitmap_search",
825 "bitmap corrupted on cg %u, start %u,"
826 " length %u, count %u, freeoff %u\n",
827 ucpi->c_cgx, start, length, count,
828 ucpi->c_freeoff);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800829 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 }
831 start = 0;
832 }
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700833 result = (start + length - loc) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 ucpi->c_frotor = result;
835
836 /*
837 * found the byte in the map
838 */
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700839
840 for (end = result + 8; result < end; result += uspi->s_fpb) {
841 blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result);
842 blockmap <<= 1;
843 mask = mask_arr[count];
844 want = want_arr[count];
845 for (pos = 0; pos <= uspi->s_fpb - count; pos++) {
846 if ((blockmap & mask) == want) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800847 UFSD("EXIT, result %llu\n",
848 (unsigned long long)result);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700849 return result + pos;
850 }
851 mask <<= 1;
852 want <<= 1;
853 }
854 }
855
856 ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n",
857 ucpi->c_cgx);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700858 UFSD("EXIT (FAILED)\n");
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800859 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860}
861
862static void ufs_clusteracct(struct super_block * sb,
863 struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
864{
865 struct ufs_sb_private_info * uspi;
866 int i, start, end, forw, back;
867
868 uspi = UFS_SB(sb)->s_uspi;
869 if (uspi->s_contigsumsize <= 0)
870 return;
871
872 if (cnt > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700873 ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 else
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700875 ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
877 /*
878 * Find the size of the cluster going forward.
879 */
880 start = blkno + 1;
881 end = start + uspi->s_contigsumsize;
882 if ( end >= ucpi->c_nclusterblks)
883 end = ucpi->c_nclusterblks;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700884 i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 if (i > end)
886 i = end;
887 forw = i - start;
888
889 /*
890 * Find the size of the cluster going backward.
891 */
892 start = blkno - 1;
893 end = start - uspi->s_contigsumsize;
894 if (end < 0 )
895 end = -1;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700896 i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 if ( i < end)
898 i = end;
899 back = start - i;
900
901 /*
902 * Account for old cluster and the possibly new forward and
903 * back clusters.
904 */
905 i = back + forw + 1;
906 if (i > uspi->s_contigsumsize)
907 i = uspi->s_contigsumsize;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700908 fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 if (back > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700910 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 if (forw > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700912 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913}
914
915
916static unsigned char ufs_fragtable_8fpb[] = {
917 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
918 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
919 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
920 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
921 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
922 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
923 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
924 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
925 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
926 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
927 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
928 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
929 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
930 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
931 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
932 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
933};
934
935static unsigned char ufs_fragtable_other[] = {
936 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
937 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
938 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
939 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
940 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
941 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
942 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
943 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
944 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
945 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
946 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
947 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
948 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
949 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
950 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
951 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
952};