blob: 048484fb10d28f12722052955abb043ecc0ac096 [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));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700118 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700119 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 }
121 sb->s_dirt = 1;
122
123 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700124 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 return;
126
127failed:
128 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700129 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 return;
131}
132
133/*
134 * Free 'count' fragments from fragment number 'fragment' (free whole blocks)
135 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800136void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700137{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 struct super_block * sb;
139 struct ufs_sb_private_info * uspi;
140 struct ufs_super_block_first * usb1;
141 struct ufs_cg_private_info * ucpi;
142 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800143 unsigned overflow, cgno, bit, end_bit, i;
144 u64 blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146 sb = inode->i_sb;
147 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300148 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800150 UFSD("ENTER, fragment %llu, count %u\n",
151 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
154 ufs_error (sb, "ufs_free_blocks", "internal error, "
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800155 "fragment %llu, count %u\n",
156 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 goto failed;
158 }
159
160 lock_super(sb);
161
162do_more:
163 overflow = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800164 cgno = ufs_dtog(uspi, fragment);
165 bit = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (cgno >= uspi->s_ncg) {
167 ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700168 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 }
170 end_bit = bit + count;
171 if (end_bit > uspi->s_fpg) {
172 overflow = bit + count - uspi->s_fpg;
173 count -= overflow;
174 end_bit -= overflow;
175 }
176
177 ucpi = ufs_load_cylinder (sb, cgno);
178 if (!ucpi)
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700179 goto failed_unlock;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700180 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 if (!ufs_cg_chkmagic(sb, ucg)) {
182 ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700183 goto failed_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 }
185
186 for (i = bit; i < end_bit; i += uspi->s_fpb) {
187 blkno = ufs_fragstoblks(i);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700188 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
190 }
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700191 ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
193 ufs_clusteracct (sb, ucpi, blkno, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
195 fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700196 uspi->cs_total.cs_nbfree++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800198
199 if (uspi->fs_magic != UFS2_MAGIC) {
200 unsigned cylno = ufs_cbtocylno(i);
201
202 fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
203 ufs_cbtorpos(i)), 1);
204 fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
205 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 }
207
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700208 ubh_mark_buffer_dirty (USPI_UBH(uspi));
209 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700211 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700212 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 }
214
215 if (overflow) {
216 fragment += count;
217 count = overflow;
218 goto do_more;
219 }
220
221 sb->s_dirt = 1;
222 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700223 UFSD("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 return;
225
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700226failed_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 unlock_super (sb);
Evgeniy Dushistov2e006392006-06-25 05:47:26 -0700228failed:
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700229 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 return;
231}
232
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700233/*
234 * Modify inode page cache in such way:
235 * have - blocks with b_blocknr equal to oldb...oldb+count-1
236 * get - blocks with b_blocknr equal to newb...newb+count-1
237 * also we suppose that oldb...oldb+count-1 blocks
238 * situated at the end of file.
239 *
240 * We can come here from ufs_writepage or ufs_prepare_write,
241 * locked_page is argument of these functions, so we already lock it.
242 */
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800243static void ufs_change_blocknr(struct inode *inode, sector_t beg,
244 unsigned int count, sector_t oldb,
245 sector_t newb, struct page *locked_page)
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700246{
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800247 const unsigned blks_per_page =
248 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits);
249 const unsigned mask = blks_per_page - 1;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800250 struct address_space * const mapping = inode->i_mapping;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800251 pgoff_t index, cur_index, last_index;
252 unsigned pos, j, lblock;
253 sector_t end, i;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700254 struct page *page;
255 struct buffer_head *head, *bh;
256
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800257 UFSD("ENTER, ino %lu, count %u, oldb %llu, newb %llu\n",
258 inode->i_ino, count,
259 (unsigned long long)oldb, (unsigned long long)newb);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700260
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800261 BUG_ON(!locked_page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700262 BUG_ON(!PageLocked(locked_page));
263
Evgeniy Dushistova685e262007-01-29 13:19:54 -0800264 cur_index = locked_page->index;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800265 end = count + beg;
266 last_index = end >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
267 for (i = beg; i < end; i = (i | mask) + 1) {
268 index = i >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700269
270 if (likely(cur_index != index)) {
271 page = ufs_get_locked_page(mapping, index);
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800272 if (!page)/* it was truncated */
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700273 continue;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800274 if (IS_ERR(page)) {/* or EIO */
Harvey Harrison9746077a72008-04-28 02:16:17 -0700275 ufs_error(inode->i_sb, __func__,
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800276 "read of page %llu failed\n",
277 (unsigned long long)index);
278 continue;
279 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700280 } else
281 page = locked_page;
282
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700283 head = page_buffers(page);
284 bh = head;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800285 pos = i & mask;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800286 for (j = 0; j < pos; ++j)
287 bh = bh->b_this_page;
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800288
289
290 if (unlikely(index == last_index))
291 lblock = end & mask;
292 else
293 lblock = blks_per_page;
294
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700295 do {
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800296 if (j >= lblock)
297 break;
298 pos = (i - beg) + j;
299
300 if (!buffer_mapped(bh))
301 map_bh(bh, inode->i_sb, oldb + pos);
302 if (!buffer_uptodate(bh)) {
303 ll_rw_block(READ, 1, &bh);
304 wait_on_buffer(bh);
305 if (!buffer_uptodate(bh)) {
Harvey Harrison9746077a72008-04-28 02:16:17 -0700306 ufs_error(inode->i_sb, __func__,
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800307 "read of block failed\n");
308 break;
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800309 }
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700310 }
311
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800312 UFSD(" change from %llu to %llu, pos %u\n",
Andrew Morton9df13032008-03-19 17:01:05 -0700313 (unsigned long long)(pos + oldb),
314 (unsigned long long)(pos + newb), pos);
Evgeniy Dushistov5431bf92007-03-16 13:38:08 -0800315
316 bh->b_blocknr = newb + pos;
317 unmap_underlying_metadata(bh->b_bdev,
318 bh->b_blocknr);
319 mark_buffer_dirty(bh);
320 ++j;
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700321 bh = bh->b_this_page;
322 } while (bh != head);
323
Evgeniy Dushistov10e5dce2006-07-01 04:36:24 -0700324 if (likely(cur_index != index))
325 ufs_put_locked_page(page);
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700326 }
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700327 UFSD("EXIT\n");
Evgeniy Dushistov6ef4d6b2006-06-25 05:47:20 -0700328}
329
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800330static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
331 int sync)
332{
333 struct buffer_head *bh;
334 sector_t end = beg + n;
335
336 for (; beg < end; ++beg) {
337 bh = sb_getblk(inode->i_sb, beg);
338 lock_buffer(bh);
339 memset(bh->b_data, 0, inode->i_sb->s_blocksize);
340 set_buffer_uptodate(bh);
341 mark_buffer_dirty(bh);
342 unlock_buffer(bh);
343 if (IS_SYNC(inode) || sync)
344 sync_dirty_buffer(bh);
345 brelse(bh);
346 }
347}
348
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800349u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment,
350 u64 goal, unsigned count, int *err,
351 struct page *locked_page)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352{
353 struct super_block * sb;
354 struct ufs_sb_private_info * uspi;
355 struct ufs_super_block_first * usb1;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800356 unsigned cgno, oldcount, newcount;
357 u64 tmp, request, result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800359 UFSD("ENTER, ino %lu, fragment %llu, goal %llu, count %u\n",
360 inode->i_ino, (unsigned long long)fragment,
361 (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 sb = inode->i_sb;
364 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300365 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 *err = -ENOSPC;
367
368 lock_super (sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800369 tmp = ufs_data_ptr_to_cpu(sb, p);
370
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800372 ufs_warning(sb, "ufs_new_fragments", "internal warning"
373 " fragment %llu, count %u",
374 (unsigned long long)fragment, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 count = uspi->s_fpb - ufs_fragnum(fragment);
376 }
377 oldcount = ufs_fragnum (fragment);
378 newcount = oldcount + count;
379
380 /*
381 * Somebody else has just allocated our fragments
382 */
383 if (oldcount) {
384 if (!tmp) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800385 ufs_error(sb, "ufs_new_fragments", "internal error, "
386 "fragment %llu, tmp %llu\n",
387 (unsigned long long)fragment,
388 (unsigned long long)tmp);
389 unlock_super(sb);
390 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 }
392 if (fragment < UFS_I(inode)->i_lastfrag) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700393 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 unlock_super (sb);
395 return 0;
396 }
397 }
398 else {
399 if (tmp) {
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700400 UFSD("EXIT (ALREADY ALLOCATED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 unlock_super(sb);
402 return 0;
403 }
404 }
405
406 /*
407 * There is not enough space for user on the device
408 */
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700409 if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(uspi, UFS_MINFREE) <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 unlock_super (sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700411 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 return 0;
413 }
414
415 if (goal >= uspi->s_size)
416 goal = 0;
417 if (goal == 0)
418 cgno = ufs_inotocg (inode->i_ino);
419 else
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800420 cgno = ufs_dtog(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
422 /*
423 * allocate new fragment
424 */
425 if (oldcount == 0) {
426 result = ufs_alloc_fragments (inode, cgno, goal, count, err);
427 if (result) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800428 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 *err = 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800430 UFS_I(inode)->i_lastfrag =
431 max_t(u32, UFS_I(inode)->i_lastfrag,
432 fragment + count);
433 ufs_clear_frags(inode, result + oldcount,
434 newcount - oldcount, locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 }
436 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800437 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 return result;
439 }
440
441 /*
442 * resize block
443 */
444 result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
445 if (result) {
446 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Evgeniy Dushistovd63b7092007-01-05 16:37:04 -0800448 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
449 locked_page != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 unlock_super(sb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800451 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 return result;
453 }
454
455 /*
456 * allocate new block and move data
457 */
458 switch (fs32_to_cpu(sb, usb1->fs_optim)) {
459 case UFS_OPTSPACE:
460 request = newcount;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700461 if (uspi->s_minfree < 5 || uspi->cs_total.cs_nffree
462 > uspi->s_dsize * uspi->s_minfree / (2 * 100))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 break;
464 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
465 break;
466 default:
467 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
468
469 case UFS_OPTTIME:
470 request = uspi->s_fpb;
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700471 if (uspi->cs_total.cs_nffree < uspi->s_dsize *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 (uspi->s_minfree - 2) / 100)
473 break;
474 usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
475 break;
476 }
477 result = ufs_alloc_fragments (inode, cgno, goal, request, err);
478 if (result) {
Evgeniy Dushistovefee2b82007-01-29 13:19:56 -0800479 ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
480 locked_page != NULL);
Evgeniy Dushistov4b25a372007-03-16 13:38:09 -0800481 ufs_change_blocknr(inode, fragment - oldcount, oldcount,
482 uspi->s_sbbase + tmp,
483 uspi->s_sbbase + result, locked_page);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800484 ufs_cpu_to_data_ptr(sb, p, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 *err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 unlock_super(sb);
488 if (newcount < request)
489 ufs_free_fragments (inode, result + newcount, request - newcount);
490 ufs_free_fragments (inode, tmp, oldcount);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800491 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 return result;
493 }
494
495 unlock_super(sb);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700496 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 return 0;
498}
499
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800500static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
501 unsigned oldcount, unsigned newcount, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
503 struct super_block * sb;
504 struct ufs_sb_private_info * uspi;
505 struct ufs_super_block_first * usb1;
506 struct ufs_cg_private_info * ucpi;
507 struct ufs_cylinder_group * ucg;
508 unsigned cgno, fragno, fragoff, count, fragsize, i;
509
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800510 UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
511 (unsigned long long)fragment, oldcount, newcount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513 sb = inode->i_sb;
514 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300515 usb1 = ubh_get_usb_first (uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 count = newcount - oldcount;
517
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800518 cgno = ufs_dtog(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 if (fs32_to_cpu(sb, UFS_SB(sb)->fs_cs(cgno).cs_nffree) < count)
520 return 0;
521 if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
522 return 0;
523 ucpi = ufs_load_cylinder (sb, cgno);
524 if (!ucpi)
525 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700526 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 if (!ufs_cg_chkmagic(sb, ucg)) {
528 ufs_panic (sb, "ufs_add_fragments",
529 "internal error, bad magic number on cg %u", cgno);
530 return 0;
531 }
532
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800533 fragno = ufs_dtogd(uspi, fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 fragoff = ufs_fragnum (fragno);
535 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700536 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 return 0;
538 /*
539 * Block can be extended
540 */
541 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
542 for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700543 if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 break;
545 fragsize = i - oldcount;
546 if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
547 ufs_panic (sb, "ufs_add_fragments",
548 "internal error or corrupted bitmap on cg %u", cgno);
549 fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
550 if (fragsize != count)
551 fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
552 for (i = oldcount; i < newcount; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700553 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
556 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700557 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700559 ubh_mark_buffer_dirty (USPI_UBH(uspi));
560 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700562 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700563 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 }
565 sb->s_dirt = 1;
566
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800567 UFSD("EXIT, fragment %llu\n", (unsigned long long)fragment);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
569 return fragment;
570}
571
572#define UFS_TEST_FREE_SPACE_CG \
573 ucg = (struct ufs_cylinder_group *) UFS_SB(sb)->s_ucg[cgno]->b_data; \
574 if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
575 goto cg_found; \
576 for (k = count; k < uspi->s_fpb; k++) \
577 if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
578 goto cg_found;
579
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800580static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
581 u64 goal, unsigned count, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
583 struct super_block * sb;
584 struct ufs_sb_private_info * uspi;
585 struct ufs_super_block_first * usb1;
586 struct ufs_cg_private_info * ucpi;
587 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800588 unsigned oldcg, i, j, k, allocsize;
589 u64 result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800591 UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
592 inode->i_ino, cgno, (unsigned long long)goal, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 sb = inode->i_sb;
595 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300596 usb1 = ubh_get_usb_first(uspi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 oldcg = cgno;
598
599 /*
600 * 1. searching on preferred cylinder group
601 */
602 UFS_TEST_FREE_SPACE_CG
603
604 /*
605 * 2. quadratic rehash
606 */
607 for (j = 1; j < uspi->s_ncg; j *= 2) {
608 cgno += j;
609 if (cgno >= uspi->s_ncg)
610 cgno -= uspi->s_ncg;
611 UFS_TEST_FREE_SPACE_CG
612 }
613
614 /*
615 * 3. brute force search
616 * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
617 */
618 cgno = (oldcg + 1) % uspi->s_ncg;
619 for (j = 2; j < uspi->s_ncg; j++) {
620 cgno++;
621 if (cgno >= uspi->s_ncg)
622 cgno = 0;
623 UFS_TEST_FREE_SPACE_CG
624 }
625
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700626 UFSD("EXIT (FAILED)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 return 0;
628
629cg_found:
630 ucpi = ufs_load_cylinder (sb, cgno);
631 if (!ucpi)
632 return 0;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700633 ucg = ubh_get_ucg (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 if (!ufs_cg_chkmagic(sb, ucg))
635 ufs_panic (sb, "ufs_alloc_fragments",
636 "internal error, bad magic number on cg %u", cgno);
637 ucg->cg_time = cpu_to_fs32(sb, get_seconds());
638
639 if (count == uspi->s_fpb) {
640 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800641 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return 0;
643 goto succed;
644 }
645
646 for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
647 if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
648 break;
649
650 if (allocsize == uspi->s_fpb) {
651 result = ufs_alloccg_block (inode, ucpi, goal, err);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800652 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return 0;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800654 goal = ufs_dtogd(uspi, result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 for (i = count; i < uspi->s_fpb; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700656 ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 i = uspi->s_fpb - count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
659 fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700660 uspi->cs_total.cs_nffree += i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i);
662 fs32_add(sb, &ucg->cg_frsum[i], 1);
663 goto succed;
664 }
665
666 result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800667 if (result == INVBLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 for (i = 0; i < count; i++)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700670 ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700673 uspi->cs_total.cs_nffree -= count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
675 fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
676
677 if (count != allocsize)
678 fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
679
680succed:
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700681 ubh_mark_buffer_dirty (USPI_UBH(uspi));
682 ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 if (sb->s_flags & MS_SYNCHRONOUS) {
Evgeniy Dushistov098d5af2006-06-25 05:47:31 -0700684 ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700685 ubh_wait_on_buffer (UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 }
687 sb->s_dirt = 1;
688
689 result += cgno * uspi->s_fpg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800690 UFSD("EXIT3, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return result;
692}
693
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800694static u64 ufs_alloccg_block(struct inode *inode,
695 struct ufs_cg_private_info *ucpi,
696 u64 goal, int *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697{
698 struct super_block * sb;
699 struct ufs_sb_private_info * uspi;
700 struct ufs_super_block_first * usb1;
701 struct ufs_cylinder_group * ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800702 u64 result, blkno;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800704 UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
706 sb = inode->i_sb;
707 uspi = UFS_SB(sb)->s_uspi;
Evgeniy7b4ee732006-01-14 11:42:06 +0300708 usb1 = ubh_get_usb_first(uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700709 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 if (goal == 0) {
712 goal = ucpi->c_rotor;
713 goto norot;
714 }
715 goal = ufs_blknum (goal);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800716 goal = ufs_dtogd(uspi, goal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
718 /*
719 * If the requested block is available, use it.
720 */
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700721 if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 result = goal;
723 goto gotit;
724 }
725
726norot:
727 result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800728 if (result == INVBLOCK)
729 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 ucpi->c_rotor = result;
731gotit:
732 blkno = ufs_fragstoblks(result);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700733 ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
735 ufs_clusteracct (sb, ucpi, blkno, -1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
737 fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
Evgeniy Dushistovee3ffd62006-06-25 05:47:30 -0700738 uspi->cs_total.cs_nbfree--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800740
741 if (uspi->fs_magic != UFS2_MAGIC) {
742 unsigned cylno = ufs_cbtocylno((unsigned)result);
743
744 fs16_sub(sb, &ubh_cg_blks(ucpi, cylno,
745 ufs_cbtorpos((unsigned)result)), 1);
746 fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1);
747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800749 UFSD("EXIT, result %llu\n", (unsigned long long)result);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 return result;
752}
753
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700754static unsigned ubh_scanc(struct ufs_sb_private_info *uspi,
755 struct ufs_buffer_head *ubh,
756 unsigned begin, unsigned size,
757 unsigned char *table, unsigned char mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758{
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700759 unsigned rest, offset;
760 unsigned char *cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700763 offset = begin & ~uspi->s_fmask;
764 begin >>= uspi->s_fshift;
765 for (;;) {
766 if ((offset + size) < uspi->s_fsize)
767 rest = size;
768 else
769 rest = uspi->s_fsize - offset;
770 size -= rest;
771 cp = ubh->bh[begin]->b_data + offset;
772 while ((table[*cp++] & mask) == 0 && --rest)
773 ;
774 if (rest || !size)
775 break;
776 begin++;
777 offset = 0;
778 }
779 return (size + rest);
780}
781
782/*
783 * Find a block of the specified size in the specified cylinder group.
784 * @sp: pointer to super block
785 * @ucpi: pointer to cylinder group info
786 * @goal: near which block we want find new one
787 * @count: specified size
788 */
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800789static u64 ufs_bitmap_search(struct super_block *sb,
790 struct ufs_cg_private_info *ucpi,
791 u64 goal, unsigned count)
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700792{
793 /*
794 * Bit patterns for identifying fragments in the block map
795 * used as ((map & mask_arr) == want_arr)
796 */
797 static const int mask_arr[9] = {
798 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
799 };
800 static const int want_arr[9] = {
801 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
802 };
803 struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
804 struct ufs_super_block_first *usb1;
805 struct ufs_cylinder_group *ucg;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800806 unsigned start, length, loc;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700807 unsigned pos, want, blockmap, mask, end;
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800808 u64 result;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700809
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800810 UFSD("ENTER, cg %u, goal %llu, count %u\n", ucpi->c_cgx,
811 (unsigned long long)goal, count);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700812
Evgeniy7b4ee732006-01-14 11:42:06 +0300813 usb1 = ubh_get_usb_first (uspi);
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700814 ucg = ubh_get_ucg(UCPI_UBH(ucpi));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 if (goal)
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800817 start = ufs_dtogd(uspi, goal) >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 else
819 start = ucpi->c_frotor >> 3;
820
821 length = ((uspi->s_fpg + 7) >> 3) - start;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700822 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
824 1 << (count - 1 + (uspi->s_fpb & 7)));
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700825 if (loc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 length = start + 1;
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700827 loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length,
828 (uspi->s_fpb == 8) ? ufs_fragtable_8fpb :
829 ufs_fragtable_other,
830 1 << (count - 1 + (uspi->s_fpb & 7)));
831 if (loc == 0) {
832 ufs_error(sb, "ufs_bitmap_search",
833 "bitmap corrupted on cg %u, start %u,"
834 " length %u, count %u, freeoff %u\n",
835 ucpi->c_cgx, start, length, count,
836 ucpi->c_freeoff);
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800837 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 }
839 start = 0;
840 }
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700841 result = (start + length - loc) << 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 ucpi->c_frotor = result;
843
844 /*
845 * found the byte in the map
846 */
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700847
848 for (end = result + 8; result < end; result += uspi->s_fpb) {
849 blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result);
850 blockmap <<= 1;
851 mask = mask_arr[count];
852 want = want_arr[count];
853 for (pos = 0; pos <= uspi->s_fpb - count; pos++) {
854 if ((blockmap & mask) == want) {
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800855 UFSD("EXIT, result %llu\n",
856 (unsigned long long)result);
Evgeniy Dushistov3e41f592006-06-25 05:47:23 -0700857 return result + pos;
858 }
859 mask <<= 1;
860 want <<= 1;
861 }
862 }
863
864 ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n",
865 ucpi->c_cgx);
Evgeniy Dushistovabf5d152006-06-25 05:47:24 -0700866 UFSD("EXIT (FAILED)\n");
Evgeniy Dushistov54fb9962007-02-12 00:54:32 -0800867 return INVBLOCK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868}
869
870static void ufs_clusteracct(struct super_block * sb,
871 struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
872{
873 struct ufs_sb_private_info * uspi;
874 int i, start, end, forw, back;
875
876 uspi = UFS_SB(sb)->s_uspi;
877 if (uspi->s_contigsumsize <= 0)
878 return;
879
880 if (cnt > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700881 ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 else
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700883 ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 /*
886 * Find the size of the cluster going forward.
887 */
888 start = blkno + 1;
889 end = start + uspi->s_contigsumsize;
890 if ( end >= ucpi->c_nclusterblks)
891 end = ucpi->c_nclusterblks;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700892 i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 if (i > end)
894 i = end;
895 forw = i - start;
896
897 /*
898 * Find the size of the cluster going backward.
899 */
900 start = blkno - 1;
901 end = start - uspi->s_contigsumsize;
902 if (end < 0 )
903 end = -1;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700904 i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 if ( i < end)
906 i = end;
907 back = start - i;
908
909 /*
910 * Account for old cluster and the possibly new forward and
911 * back clusters.
912 */
913 i = back + forw + 1;
914 if (i > uspi->s_contigsumsize)
915 i = uspi->s_contigsumsize;
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700916 fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 if (back > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700918 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 if (forw > 0)
Evgeniy Dushistov9695ef12006-06-25 05:47:22 -0700920 fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921}
922
923
924static unsigned char ufs_fragtable_8fpb[] = {
925 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
926 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
927 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
928 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
929 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
930 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
931 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
932 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
933 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
934 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
935 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
936 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
937 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
938 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
939 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
940 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
941};
942
943static unsigned char ufs_fragtable_other[] = {
944 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
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 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
948 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
949 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
950 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
951 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
952 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
953 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
954 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
955 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
956 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
957 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
958 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
959 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
960};