2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* linux/fs/ufs/balloc.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 1998
|
|
|
|
* Daniel Pirkl <daniel.pirkl@email.cz>
|
|
|
|
* Charles University, Faculty of Mathematics and Physics
|
2007-02-12 09:54:32 +01:00
|
|
|
*
|
|
|
|
* UFS2 write support Evgeniy Dushistov <dushistov@mail.ru>, 2007
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/stat.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/buffer_head.h>
|
2006-01-11 21:17:46 +01:00
|
|
|
#include <linux/capability.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
|
2008-02-08 13:21:31 +01:00
|
|
|
#include "ufs_fs.h"
|
2007-10-17 08:26:51 +02:00
|
|
|
#include "ufs.h"
|
2005-04-17 00:20:36 +02:00
|
|
|
#include "swab.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
#define INVBLOCK ((u64)-1L)
|
|
|
|
|
|
|
|
static u64 ufs_add_fragments(struct inode *, u64, unsigned, unsigned, int *);
|
|
|
|
static u64 ufs_alloc_fragments(struct inode *, unsigned, u64, unsigned, int *);
|
|
|
|
static u64 ufs_alloccg_block(struct inode *, struct ufs_cg_private_info *, u64, int *);
|
|
|
|
static u64 ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, u64, unsigned);
|
2005-04-17 00:20:36 +02:00
|
|
|
static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[];
|
|
|
|
static void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free 'count' fragments from fragment number 'fragment'
|
|
|
|
*/
|
2007-02-12 09:54:32 +01:00
|
|
|
void ufs_free_fragments(struct inode *inode, u64 fragment, unsigned count)
|
2006-06-25 14:47:20 +02:00
|
|
|
{
|
2005-04-17 00:20:36 +02:00
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
|
|
|
struct ufs_cg_private_info * ucpi;
|
|
|
|
struct ufs_cylinder_group * ucg;
|
2007-02-12 09:54:32 +01:00
|
|
|
unsigned cgno, bit, end_bit, bbase, blkmap, i;
|
|
|
|
u64 blkno;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first(uspi);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, fragment %llu, count %u\n",
|
|
|
|
(unsigned long long)fragment, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (ufs_fragnum(fragment) + count > uspi->s_fpg)
|
|
|
|
ufs_error (sb, "ufs_free_fragments", "internal error");
|
|
|
|
|
|
|
|
lock_super(sb);
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
cgno = ufs_dtog(uspi, fragment);
|
|
|
|
bit = ufs_dtogd(uspi, fragment);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (cgno >= uspi->s_ncg) {
|
|
|
|
ufs_panic (sb, "ufs_free_fragments", "freeing blocks are outside device");
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
ucpi = ufs_load_cylinder (sb, cgno);
|
|
|
|
if (!ucpi)
|
|
|
|
goto failed;
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (!ufs_cg_chkmagic(sb, ucg)) {
|
|
|
|
ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno);
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
end_bit = bit + count;
|
|
|
|
bbase = ufs_blknum (bit);
|
2006-06-25 14:47:22 +02:00
|
|
|
blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
|
2005-04-17 00:20:36 +02:00
|
|
|
ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1);
|
|
|
|
for (i = bit; i < end_bit; i++) {
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, i))
|
|
|
|
ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, i);
|
2006-01-14 09:42:06 +01:00
|
|
|
else
|
|
|
|
ufs_error (sb, "ufs_free_fragments",
|
|
|
|
"bit already cleared for fragment %u", i);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nffree += count;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
|
2006-06-25 14:47:22 +02:00
|
|
|
blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase);
|
2005-04-17 00:20:36 +02:00
|
|
|
ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trying to reassemble free fragments into block
|
|
|
|
*/
|
|
|
|
blkno = ufs_fragstoblks (bbase);
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nffree -= uspi->s_fpb;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb);
|
|
|
|
if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
|
|
|
|
ufs_clusteracct (sb, ucpi, blkno, 1);
|
|
|
|
fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nbfree++;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
|
2007-02-12 09:54:32 +01:00
|
|
|
if (uspi->fs_magic != UFS2_MAGIC) {
|
|
|
|
unsigned cylno = ufs_cbtocylno (bbase);
|
|
|
|
|
|
|
|
fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
|
|
|
|
ufs_cbtorpos(bbase)), 1);
|
|
|
|
fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_mark_buffer_dirty (USPI_UBH(uspi));
|
|
|
|
ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
2006-06-25 14:47:31 +02:00
|
|
|
ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_wait_on_buffer (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
sb->s_dirt = 1;
|
|
|
|
|
|
|
|
unlock_super (sb);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
failed:
|
|
|
|
unlock_super (sb);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free 'count' fragments from fragment number 'fragment' (free whole blocks)
|
|
|
|
*/
|
2007-02-12 09:54:32 +01:00
|
|
|
void ufs_free_blocks(struct inode *inode, u64 fragment, unsigned count)
|
2006-06-25 14:47:20 +02:00
|
|
|
{
|
2005-04-17 00:20:36 +02:00
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
|
|
|
struct ufs_cg_private_info * ucpi;
|
|
|
|
struct ufs_cylinder_group * ucg;
|
2007-02-12 09:54:32 +01:00
|
|
|
unsigned overflow, cgno, bit, end_bit, i;
|
|
|
|
u64 blkno;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first(uspi);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, fragment %llu, count %u\n",
|
|
|
|
(unsigned long long)fragment, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
|
|
|
|
ufs_error (sb, "ufs_free_blocks", "internal error, "
|
2007-02-12 09:54:32 +01:00
|
|
|
"fragment %llu, count %u\n",
|
|
|
|
(unsigned long long)fragment, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
lock_super(sb);
|
|
|
|
|
|
|
|
do_more:
|
|
|
|
overflow = 0;
|
2007-02-12 09:54:32 +01:00
|
|
|
cgno = ufs_dtog(uspi, fragment);
|
|
|
|
bit = ufs_dtogd(uspi, fragment);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (cgno >= uspi->s_ncg) {
|
|
|
|
ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
|
2006-06-25 14:47:26 +02:00
|
|
|
goto failed_unlock;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
end_bit = bit + count;
|
|
|
|
if (end_bit > uspi->s_fpg) {
|
|
|
|
overflow = bit + count - uspi->s_fpg;
|
|
|
|
count -= overflow;
|
|
|
|
end_bit -= overflow;
|
|
|
|
}
|
|
|
|
|
|
|
|
ucpi = ufs_load_cylinder (sb, cgno);
|
|
|
|
if (!ucpi)
|
2006-06-25 14:47:26 +02:00
|
|
|
goto failed_unlock;
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (!ufs_cg_chkmagic(sb, ucg)) {
|
|
|
|
ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
|
2006-06-25 14:47:26 +02:00
|
|
|
goto failed_unlock;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = bit; i < end_bit; i += uspi->s_fpb) {
|
|
|
|
blkno = ufs_fragstoblks(i);
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
|
|
|
|
}
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
|
2005-04-17 00:20:36 +02:00
|
|
|
if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
|
|
|
|
ufs_clusteracct (sb, ucpi, blkno, 1);
|
|
|
|
|
|
|
|
fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nbfree++;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1);
|
2007-02-12 09:54:32 +01:00
|
|
|
|
|
|
|
if (uspi->fs_magic != UFS2_MAGIC) {
|
|
|
|
unsigned cylno = ufs_cbtocylno(i);
|
|
|
|
|
|
|
|
fs16_add(sb, &ubh_cg_blks(ucpi, cylno,
|
|
|
|
ufs_cbtorpos(i)), 1);
|
|
|
|
fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_mark_buffer_dirty (USPI_UBH(uspi));
|
|
|
|
ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
2006-06-25 14:47:31 +02:00
|
|
|
ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_wait_on_buffer (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (overflow) {
|
|
|
|
fragment += count;
|
|
|
|
count = overflow;
|
|
|
|
goto do_more;
|
|
|
|
}
|
|
|
|
|
|
|
|
sb->s_dirt = 1;
|
|
|
|
unlock_super (sb);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
|
2006-06-25 14:47:26 +02:00
|
|
|
failed_unlock:
|
2005-04-17 00:20:36 +02:00
|
|
|
unlock_super (sb);
|
2006-06-25 14:47:26 +02:00
|
|
|
failed:
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-06-25 14:47:20 +02:00
|
|
|
/*
|
|
|
|
* Modify inode page cache in such way:
|
|
|
|
* have - blocks with b_blocknr equal to oldb...oldb+count-1
|
|
|
|
* get - blocks with b_blocknr equal to newb...newb+count-1
|
|
|
|
* also we suppose that oldb...oldb+count-1 blocks
|
|
|
|
* situated at the end of file.
|
|
|
|
*
|
|
|
|
* We can come here from ufs_writepage or ufs_prepare_write,
|
|
|
|
* locked_page is argument of these functions, so we already lock it.
|
|
|
|
*/
|
2007-03-16 22:38:08 +01:00
|
|
|
static void ufs_change_blocknr(struct inode *inode, sector_t beg,
|
|
|
|
unsigned int count, sector_t oldb,
|
|
|
|
sector_t newb, struct page *locked_page)
|
2006-06-25 14:47:20 +02:00
|
|
|
{
|
2007-03-16 22:38:08 +01:00
|
|
|
const unsigned blks_per_page =
|
|
|
|
1 << (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
const unsigned mask = blks_per_page - 1;
|
2007-01-29 22:19:56 +01:00
|
|
|
struct address_space * const mapping = inode->i_mapping;
|
2007-03-16 22:38:08 +01:00
|
|
|
pgoff_t index, cur_index, last_index;
|
|
|
|
unsigned pos, j, lblock;
|
|
|
|
sector_t end, i;
|
2006-06-25 14:47:20 +02:00
|
|
|
struct page *page;
|
|
|
|
struct buffer_head *head, *bh;
|
|
|
|
|
2007-03-16 22:38:08 +01:00
|
|
|
UFSD("ENTER, ino %lu, count %u, oldb %llu, newb %llu\n",
|
|
|
|
inode->i_ino, count,
|
|
|
|
(unsigned long long)oldb, (unsigned long long)newb);
|
2006-06-25 14:47:20 +02:00
|
|
|
|
2007-01-29 22:19:54 +01:00
|
|
|
BUG_ON(!locked_page);
|
2006-06-25 14:47:20 +02:00
|
|
|
BUG_ON(!PageLocked(locked_page));
|
|
|
|
|
2007-01-29 22:19:54 +01:00
|
|
|
cur_index = locked_page->index;
|
2007-03-16 22:38:08 +01:00
|
|
|
end = count + beg;
|
|
|
|
last_index = end >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
|
|
|
for (i = beg; i < end; i = (i | mask) + 1) {
|
|
|
|
index = i >> (PAGE_CACHE_SHIFT - inode->i_blkbits);
|
2006-06-25 14:47:20 +02:00
|
|
|
|
|
|
|
if (likely(cur_index != index)) {
|
|
|
|
page = ufs_get_locked_page(mapping, index);
|
2007-03-16 22:38:08 +01:00
|
|
|
if (!page)/* it was truncated */
|
|
|
|
continue;
|
|
|
|
if (IS_ERR(page)) {/* or EIO */
|
2008-04-28 11:16:17 +02:00
|
|
|
ufs_error(inode->i_sb, __func__,
|
2007-03-16 22:38:08 +01:00
|
|
|
"read of page %llu failed\n",
|
|
|
|
(unsigned long long)index);
|
2006-06-25 14:47:20 +02:00
|
|
|
continue;
|
2007-03-16 22:38:08 +01:00
|
|
|
}
|
2006-06-25 14:47:20 +02:00
|
|
|
} else
|
|
|
|
page = locked_page;
|
|
|
|
|
|
|
|
head = page_buffers(page);
|
|
|
|
bh = head;
|
2007-03-16 22:38:08 +01:00
|
|
|
pos = i & mask;
|
2007-01-29 22:19:56 +01:00
|
|
|
for (j = 0; j < pos; ++j)
|
|
|
|
bh = bh->b_this_page;
|
2007-03-16 22:38:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
if (unlikely(index == last_index))
|
|
|
|
lblock = end & mask;
|
|
|
|
else
|
|
|
|
lblock = blks_per_page;
|
|
|
|
|
2006-06-25 14:47:20 +02:00
|
|
|
do {
|
2007-03-16 22:38:08 +01:00
|
|
|
if (j >= lblock)
|
|
|
|
break;
|
|
|
|
pos = (i - beg) + j;
|
|
|
|
|
|
|
|
if (!buffer_mapped(bh))
|
|
|
|
map_bh(bh, inode->i_sb, oldb + pos);
|
|
|
|
if (!buffer_uptodate(bh)) {
|
|
|
|
ll_rw_block(READ, 1, &bh);
|
|
|
|
wait_on_buffer(bh);
|
|
|
|
if (!buffer_uptodate(bh)) {
|
2008-04-28 11:16:17 +02:00
|
|
|
ufs_error(inode->i_sb, __func__,
|
2007-03-16 22:38:08 +01:00
|
|
|
"read of block failed\n");
|
|
|
|
break;
|
2007-01-29 22:19:56 +01:00
|
|
|
}
|
2006-06-25 14:47:20 +02:00
|
|
|
}
|
|
|
|
|
2007-03-16 22:38:08 +01:00
|
|
|
UFSD(" change from %llu to %llu, pos %u\n",
|
2008-03-20 01:01:05 +01:00
|
|
|
(unsigned long long)(pos + oldb),
|
|
|
|
(unsigned long long)(pos + newb), pos);
|
2007-03-16 22:38:08 +01:00
|
|
|
|
|
|
|
bh->b_blocknr = newb + pos;
|
|
|
|
unmap_underlying_metadata(bh->b_bdev,
|
|
|
|
bh->b_blocknr);
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
++j;
|
2006-06-25 14:47:20 +02:00
|
|
|
bh = bh->b_this_page;
|
|
|
|
} while (bh != head);
|
|
|
|
|
2006-07-01 13:36:24 +02:00
|
|
|
if (likely(cur_index != index))
|
|
|
|
ufs_put_locked_page(page);
|
2006-06-25 14:47:20 +02:00
|
|
|
}
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT\n");
|
2006-06-25 14:47:20 +02:00
|
|
|
}
|
|
|
|
|
[PATCH] fix garbage instead of zeroes in UFS
Looks like this is the problem, which point Al Viro some time ago:
ufs's get_block callback allocates 16k of disk at a time, and links that
entire 16k into the file's metadata. But because get_block is called for only
a single buffer_head (a 2k buffer_head in this case?) we are only able to tell
the VFS that this 2k is buffer_new().
So when ufs_getfrag_block() is later called to map some more data in the file,
and when that data resides within the remaining 14k of this fragment,
ufs_getfrag_block() will incorrectly return a !buffer_new() buffer_head.
I don't see _right_ way to do nullification of whole block, if use inode
page cache, some pages may be outside of inode limits (inode size), and
will be lost; if use blockdev page cache it is possible to zero real data,
if later inode page cache will be used.
The simpliest way, as can I see usage of block device page cache, but not only
mark dirty, but also sync it during "nullification". I use my simple tests
collection, which I used for check that create,open,write,read,close works on
ufs, and I see that this patch makes ufs code 18% slower then before.
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2007-01-06 01:37:04 +01:00
|
|
|
static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n,
|
|
|
|
int sync)
|
|
|
|
{
|
|
|
|
struct buffer_head *bh;
|
|
|
|
sector_t end = beg + n;
|
|
|
|
|
|
|
|
for (; beg < end; ++beg) {
|
|
|
|
bh = sb_getblk(inode->i_sb, beg);
|
|
|
|
lock_buffer(bh);
|
|
|
|
memset(bh->b_data, 0, inode->i_sb->s_blocksize);
|
|
|
|
set_buffer_uptodate(bh);
|
|
|
|
mark_buffer_dirty(bh);
|
|
|
|
unlock_buffer(bh);
|
|
|
|
if (IS_SYNC(inode) || sync)
|
|
|
|
sync_dirty_buffer(bh);
|
|
|
|
brelse(bh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
u64 ufs_new_fragments(struct inode *inode, void *p, u64 fragment,
|
|
|
|
u64 goal, unsigned count, int *err,
|
|
|
|
struct page *locked_page)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
2007-02-12 09:54:32 +01:00
|
|
|
unsigned cgno, oldcount, newcount;
|
|
|
|
u64 tmp, request, result;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, ino %lu, fragment %llu, goal %llu, count %u\n",
|
|
|
|
inode->i_ino, (unsigned long long)fragment,
|
|
|
|
(unsigned long long)goal, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first(uspi);
|
2005-04-17 00:20:36 +02:00
|
|
|
*err = -ENOSPC;
|
|
|
|
|
|
|
|
lock_super (sb);
|
2007-02-12 09:54:32 +01:00
|
|
|
tmp = ufs_data_ptr_to_cpu(sb, p);
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
|
2007-02-12 09:54:32 +01:00
|
|
|
ufs_warning(sb, "ufs_new_fragments", "internal warning"
|
|
|
|
" fragment %llu, count %u",
|
|
|
|
(unsigned long long)fragment, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
count = uspi->s_fpb - ufs_fragnum(fragment);
|
|
|
|
}
|
|
|
|
oldcount = ufs_fragnum (fragment);
|
|
|
|
newcount = oldcount + count;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Somebody else has just allocated our fragments
|
|
|
|
*/
|
|
|
|
if (oldcount) {
|
|
|
|
if (!tmp) {
|
2007-02-12 09:54:32 +01:00
|
|
|
ufs_error(sb, "ufs_new_fragments", "internal error, "
|
|
|
|
"fragment %llu, tmp %llu\n",
|
|
|
|
(unsigned long long)fragment,
|
|
|
|
(unsigned long long)tmp);
|
|
|
|
unlock_super(sb);
|
|
|
|
return INVBLOCK;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
if (fragment < UFS_I(inode)->i_lastfrag) {
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (ALREADY ALLOCATED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
unlock_super (sb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (tmp) {
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (ALREADY ALLOCATED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
unlock_super(sb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is not enough space for user on the device
|
|
|
|
*/
|
2006-06-25 14:47:30 +02:00
|
|
|
if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(uspi, UFS_MINFREE) <= 0) {
|
2005-04-17 00:20:36 +02:00
|
|
|
unlock_super (sb);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (goal >= uspi->s_size)
|
|
|
|
goal = 0;
|
|
|
|
if (goal == 0)
|
|
|
|
cgno = ufs_inotocg (inode->i_ino);
|
|
|
|
else
|
2007-02-12 09:54:32 +01:00
|
|
|
cgno = ufs_dtog(uspi, goal);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate new fragment
|
|
|
|
*/
|
|
|
|
if (oldcount == 0) {
|
|
|
|
result = ufs_alloc_fragments (inode, cgno, goal, count, err);
|
|
|
|
if (result) {
|
2007-02-12 09:54:32 +01:00
|
|
|
ufs_cpu_to_data_ptr(sb, p, result);
|
2005-04-17 00:20:36 +02:00
|
|
|
*err = 0;
|
2007-02-12 09:54:32 +01:00
|
|
|
UFS_I(inode)->i_lastfrag =
|
|
|
|
max_t(u32, UFS_I(inode)->i_lastfrag,
|
|
|
|
fragment + count);
|
|
|
|
ufs_clear_frags(inode, result + oldcount,
|
|
|
|
newcount - oldcount, locked_page != NULL);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
unlock_super(sb);
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, result %llu\n", (unsigned long long)result);
|
2005-04-17 00:20:36 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* resize block
|
|
|
|
*/
|
|
|
|
result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
|
|
|
|
if (result) {
|
|
|
|
*err = 0;
|
|
|
|
UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
|
[PATCH] fix garbage instead of zeroes in UFS
Looks like this is the problem, which point Al Viro some time ago:
ufs's get_block callback allocates 16k of disk at a time, and links that
entire 16k into the file's metadata. But because get_block is called for only
a single buffer_head (a 2k buffer_head in this case?) we are only able to tell
the VFS that this 2k is buffer_new().
So when ufs_getfrag_block() is later called to map some more data in the file,
and when that data resides within the remaining 14k of this fragment,
ufs_getfrag_block() will incorrectly return a !buffer_new() buffer_head.
I don't see _right_ way to do nullification of whole block, if use inode
page cache, some pages may be outside of inode limits (inode size), and
will be lost; if use blockdev page cache it is possible to zero real data,
if later inode page cache will be used.
The simpliest way, as can I see usage of block device page cache, but not only
mark dirty, but also sync it during "nullification". I use my simple tests
collection, which I used for check that create,open,write,read,close works on
ufs, and I see that this patch makes ufs code 18% slower then before.
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2007-01-06 01:37:04 +01:00
|
|
|
ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
|
|
|
|
locked_page != NULL);
|
2005-04-17 00:20:36 +02:00
|
|
|
unlock_super(sb);
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, result %llu\n", (unsigned long long)result);
|
2005-04-17 00:20:36 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* allocate new block and move data
|
|
|
|
*/
|
|
|
|
switch (fs32_to_cpu(sb, usb1->fs_optim)) {
|
|
|
|
case UFS_OPTSPACE:
|
|
|
|
request = newcount;
|
2006-06-25 14:47:30 +02:00
|
|
|
if (uspi->s_minfree < 5 || uspi->cs_total.cs_nffree
|
|
|
|
> uspi->s_dsize * uspi->s_minfree / (2 * 100))
|
2005-04-17 00:20:36 +02:00
|
|
|
break;
|
|
|
|
usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
|
|
|
|
|
|
|
|
case UFS_OPTTIME:
|
|
|
|
request = uspi->s_fpb;
|
2006-06-25 14:47:30 +02:00
|
|
|
if (uspi->cs_total.cs_nffree < uspi->s_dsize *
|
2005-04-17 00:20:36 +02:00
|
|
|
(uspi->s_minfree - 2) / 100)
|
|
|
|
break;
|
|
|
|
usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
result = ufs_alloc_fragments (inode, cgno, goal, request, err);
|
|
|
|
if (result) {
|
2007-01-29 22:19:56 +01:00
|
|
|
ufs_clear_frags(inode, result + oldcount, newcount - oldcount,
|
|
|
|
locked_page != NULL);
|
[PATCH] ufs: zeroize the rest of block in truncate
This patch fix behaviour in such test scenario:
lseek(fd, BIG_OFFSET)
write(fd, buf, sizeof(buf))
truncate(BIG_OFFSET)
truncate(BIG_OFFSET + sizeof(buf))
read(fd, buf...)
Because of if file big enough(BIG_OFFSET) we start allocate space by block,
ordinary block size > page size, so we should zeroize the rest of block in
truncate(except last framgnet, about which VFS should care), to not get
garbage, when we extend file.
Also patch corrects conversion from pointer to block to physical block number,
this helps in case of not common used UFS types.
And add to debug output inode number.
Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-03-16 22:38:09 +01:00
|
|
|
ufs_change_blocknr(inode, fragment - oldcount, oldcount,
|
|
|
|
uspi->s_sbbase + tmp,
|
|
|
|
uspi->s_sbbase + result, locked_page);
|
2007-02-12 09:54:32 +01:00
|
|
|
ufs_cpu_to_data_ptr(sb, p, result);
|
2005-04-17 00:20:36 +02:00
|
|
|
*err = 0;
|
|
|
|
UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count);
|
|
|
|
unlock_super(sb);
|
|
|
|
if (newcount < request)
|
|
|
|
ufs_free_fragments (inode, result + newcount, request - newcount);
|
|
|
|
ufs_free_fragments (inode, tmp, oldcount);
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, result %llu\n", (unsigned long long)result);
|
2005-04-17 00:20:36 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
unlock_super(sb);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
static u64 ufs_add_fragments(struct inode *inode, u64 fragment,
|
|
|
|
unsigned oldcount, unsigned newcount, int *err)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
|
|
|
struct ufs_cg_private_info * ucpi;
|
|
|
|
struct ufs_cylinder_group * ucg;
|
|
|
|
unsigned cgno, fragno, fragoff, count, fragsize, i;
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, fragment %llu, oldcount %u, newcount %u\n",
|
|
|
|
(unsigned long long)fragment, oldcount, newcount);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first (uspi);
|
2005-04-17 00:20:36 +02:00
|
|
|
count = newcount - oldcount;
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
cgno = ufs_dtog(uspi, fragment);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (fs32_to_cpu(sb, UFS_SB(sb)->fs_cs(cgno).cs_nffree) < count)
|
|
|
|
return 0;
|
|
|
|
if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
|
|
|
|
return 0;
|
|
|
|
ucpi = ufs_load_cylinder (sb, cgno);
|
|
|
|
if (!ucpi)
|
|
|
|
return 0;
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (!ufs_cg_chkmagic(sb, ucg)) {
|
|
|
|
ufs_panic (sb, "ufs_add_fragments",
|
|
|
|
"internal error, bad magic number on cg %u", cgno);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
fragno = ufs_dtogd(uspi, fragment);
|
2005-04-17 00:20:36 +02:00
|
|
|
fragoff = ufs_fragnum (fragno);
|
|
|
|
for (i = oldcount; i < newcount; i++)
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
/*
|
|
|
|
* Block can be extended
|
|
|
|
*/
|
|
|
|
ucg->cg_time = cpu_to_fs32(sb, get_seconds());
|
|
|
|
for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i))
|
2005-04-17 00:20:36 +02:00
|
|
|
break;
|
|
|
|
fragsize = i - oldcount;
|
|
|
|
if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
|
|
|
|
ufs_panic (sb, "ufs_add_fragments",
|
|
|
|
"internal error or corrupted bitmap on cg %u", cgno);
|
|
|
|
fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
|
|
|
|
if (fragsize != count)
|
|
|
|
fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
|
|
|
|
for (i = oldcount; i < newcount; i++)
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
|
|
|
|
fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nffree -= count;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_mark_buffer_dirty (USPI_UBH(uspi));
|
|
|
|
ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
2006-06-25 14:47:31 +02:00
|
|
|
ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_wait_on_buffer (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
sb->s_dirt = 1;
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, fragment %llu\n", (unsigned long long)fragment);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return fragment;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define UFS_TEST_FREE_SPACE_CG \
|
|
|
|
ucg = (struct ufs_cylinder_group *) UFS_SB(sb)->s_ucg[cgno]->b_data; \
|
|
|
|
if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
|
|
|
|
goto cg_found; \
|
|
|
|
for (k = count; k < uspi->s_fpb; k++) \
|
|
|
|
if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
|
|
|
|
goto cg_found;
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
static u64 ufs_alloc_fragments(struct inode *inode, unsigned cgno,
|
|
|
|
u64 goal, unsigned count, int *err)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
|
|
|
struct ufs_cg_private_info * ucpi;
|
|
|
|
struct ufs_cylinder_group * ucg;
|
2007-02-12 09:54:32 +01:00
|
|
|
unsigned oldcg, i, j, k, allocsize;
|
|
|
|
u64 result;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, ino %lu, cgno %u, goal %llu, count %u\n",
|
|
|
|
inode->i_ino, cgno, (unsigned long long)goal, count);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first(uspi);
|
2005-04-17 00:20:36 +02:00
|
|
|
oldcg = cgno;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 1. searching on preferred cylinder group
|
|
|
|
*/
|
|
|
|
UFS_TEST_FREE_SPACE_CG
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 2. quadratic rehash
|
|
|
|
*/
|
|
|
|
for (j = 1; j < uspi->s_ncg; j *= 2) {
|
|
|
|
cgno += j;
|
|
|
|
if (cgno >= uspi->s_ncg)
|
|
|
|
cgno -= uspi->s_ncg;
|
|
|
|
UFS_TEST_FREE_SPACE_CG
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3. brute force search
|
|
|
|
* We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
|
|
|
|
*/
|
|
|
|
cgno = (oldcg + 1) % uspi->s_ncg;
|
|
|
|
for (j = 2; j < uspi->s_ncg; j++) {
|
|
|
|
cgno++;
|
|
|
|
if (cgno >= uspi->s_ncg)
|
|
|
|
cgno = 0;
|
|
|
|
UFS_TEST_FREE_SPACE_CG
|
|
|
|
}
|
|
|
|
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
cg_found:
|
|
|
|
ucpi = ufs_load_cylinder (sb, cgno);
|
|
|
|
if (!ucpi)
|
|
|
|
return 0;
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (!ufs_cg_chkmagic(sb, ucg))
|
|
|
|
ufs_panic (sb, "ufs_alloc_fragments",
|
|
|
|
"internal error, bad magic number on cg %u", cgno);
|
|
|
|
ucg->cg_time = cpu_to_fs32(sb, get_seconds());
|
|
|
|
|
|
|
|
if (count == uspi->s_fpb) {
|
|
|
|
result = ufs_alloccg_block (inode, ucpi, goal, err);
|
2007-02-12 09:54:32 +01:00
|
|
|
if (result == INVBLOCK)
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
goto succed;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
|
|
|
|
if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (allocsize == uspi->s_fpb) {
|
|
|
|
result = ufs_alloccg_block (inode, ucpi, goal, err);
|
2007-02-12 09:54:32 +01:00
|
|
|
if (result == INVBLOCK)
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
2007-02-12 09:54:32 +01:00
|
|
|
goal = ufs_dtogd(uspi, result);
|
2005-04-17 00:20:36 +02:00
|
|
|
for (i = count; i < uspi->s_fpb; i++)
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i);
|
2005-04-17 00:20:36 +02:00
|
|
|
i = uspi->s_fpb - count;
|
|
|
|
|
|
|
|
fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nffree += i;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i);
|
|
|
|
fs32_add(sb, &ucg->cg_frsum[i], 1);
|
|
|
|
goto succed;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
|
2007-02-12 09:54:32 +01:00
|
|
|
if (result == INVBLOCK)
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
for (i = 0; i < count; i++)
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nffree -= count;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count);
|
|
|
|
fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
|
|
|
|
|
|
|
|
if (count != allocsize)
|
|
|
|
fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
|
|
|
|
|
|
|
|
succed:
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_mark_buffer_dirty (USPI_UBH(uspi));
|
|
|
|
ubh_mark_buffer_dirty (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
2006-06-25 14:47:31 +02:00
|
|
|
ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi));
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_wait_on_buffer (UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
sb->s_dirt = 1;
|
|
|
|
|
|
|
|
result += cgno * uspi->s_fpg;
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT3, result %llu\n", (unsigned long long)result);
|
2005-04-17 00:20:36 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
static u64 ufs_alloccg_block(struct inode *inode,
|
|
|
|
struct ufs_cg_private_info *ucpi,
|
|
|
|
u64 goal, int *err)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct super_block * sb;
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
struct ufs_super_block_first * usb1;
|
|
|
|
struct ufs_cylinder_group * ucg;
|
2007-02-12 09:54:32 +01:00
|
|
|
u64 result, blkno;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, goal %llu\n", (unsigned long long)goal);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
sb = inode->i_sb;
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first(uspi);
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg(UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (goal == 0) {
|
|
|
|
goal = ucpi->c_rotor;
|
|
|
|
goto norot;
|
|
|
|
}
|
|
|
|
goal = ufs_blknum (goal);
|
2007-02-12 09:54:32 +01:00
|
|
|
goal = ufs_dtogd(uspi, goal);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the requested block is available, use it.
|
|
|
|
*/
|
2006-06-25 14:47:22 +02:00
|
|
|
if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) {
|
2005-04-17 00:20:36 +02:00
|
|
|
result = goal;
|
|
|
|
goto gotit;
|
|
|
|
}
|
|
|
|
|
|
|
|
norot:
|
|
|
|
result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
|
2007-02-12 09:54:32 +01:00
|
|
|
if (result == INVBLOCK)
|
|
|
|
return INVBLOCK;
|
2005-04-17 00:20:36 +02:00
|
|
|
ucpi->c_rotor = result;
|
|
|
|
gotit:
|
|
|
|
blkno = ufs_fragstoblks(result);
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno);
|
2005-04-17 00:20:36 +02:00
|
|
|
if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
|
|
|
|
ufs_clusteracct (sb, ucpi, blkno, -1);
|
|
|
|
|
|
|
|
fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
|
2006-06-25 14:47:30 +02:00
|
|
|
uspi->cs_total.cs_nbfree--;
|
2005-04-17 00:20:36 +02:00
|
|
|
fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1);
|
2007-02-12 09:54:32 +01:00
|
|
|
|
|
|
|
if (uspi->fs_magic != UFS2_MAGIC) {
|
|
|
|
unsigned cylno = ufs_cbtocylno((unsigned)result);
|
|
|
|
|
|
|
|
fs16_sub(sb, &ubh_cg_blks(ucpi, cylno,
|
|
|
|
ufs_cbtorpos((unsigned)result)), 1);
|
|
|
|
fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, result %llu\n", (unsigned long long)result);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2006-06-25 14:47:23 +02:00
|
|
|
static unsigned ubh_scanc(struct ufs_sb_private_info *uspi,
|
|
|
|
struct ufs_buffer_head *ubh,
|
|
|
|
unsigned begin, unsigned size,
|
|
|
|
unsigned char *table, unsigned char mask)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2006-06-25 14:47:23 +02:00
|
|
|
unsigned rest, offset;
|
|
|
|
unsigned char *cp;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
|
2006-06-25 14:47:23 +02:00
|
|
|
offset = begin & ~uspi->s_fmask;
|
|
|
|
begin >>= uspi->s_fshift;
|
|
|
|
for (;;) {
|
|
|
|
if ((offset + size) < uspi->s_fsize)
|
|
|
|
rest = size;
|
|
|
|
else
|
|
|
|
rest = uspi->s_fsize - offset;
|
|
|
|
size -= rest;
|
|
|
|
cp = ubh->bh[begin]->b_data + offset;
|
|
|
|
while ((table[*cp++] & mask) == 0 && --rest)
|
|
|
|
;
|
|
|
|
if (rest || !size)
|
|
|
|
break;
|
|
|
|
begin++;
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
return (size + rest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find a block of the specified size in the specified cylinder group.
|
|
|
|
* @sp: pointer to super block
|
|
|
|
* @ucpi: pointer to cylinder group info
|
|
|
|
* @goal: near which block we want find new one
|
|
|
|
* @count: specified size
|
|
|
|
*/
|
2007-02-12 09:54:32 +01:00
|
|
|
static u64 ufs_bitmap_search(struct super_block *sb,
|
|
|
|
struct ufs_cg_private_info *ucpi,
|
|
|
|
u64 goal, unsigned count)
|
2006-06-25 14:47:23 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Bit patterns for identifying fragments in the block map
|
|
|
|
* used as ((map & mask_arr) == want_arr)
|
|
|
|
*/
|
|
|
|
static const int mask_arr[9] = {
|
|
|
|
0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff
|
|
|
|
};
|
|
|
|
static const int want_arr[9] = {
|
|
|
|
0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe
|
|
|
|
};
|
|
|
|
struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
struct ufs_super_block_first *usb1;
|
|
|
|
struct ufs_cylinder_group *ucg;
|
2007-02-12 09:54:32 +01:00
|
|
|
unsigned start, length, loc;
|
2006-06-25 14:47:23 +02:00
|
|
|
unsigned pos, want, blockmap, mask, end;
|
2007-02-12 09:54:32 +01:00
|
|
|
u64 result;
|
2006-06-25 14:47:23 +02:00
|
|
|
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("ENTER, cg %u, goal %llu, count %u\n", ucpi->c_cgx,
|
|
|
|
(unsigned long long)goal, count);
|
2006-06-25 14:47:23 +02:00
|
|
|
|
2006-01-14 09:42:06 +01:00
|
|
|
usb1 = ubh_get_usb_first (uspi);
|
2006-06-25 14:47:22 +02:00
|
|
|
ucg = ubh_get_ucg(UCPI_UBH(ucpi));
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (goal)
|
2007-02-12 09:54:32 +01:00
|
|
|
start = ufs_dtogd(uspi, goal) >> 3;
|
2005-04-17 00:20:36 +02:00
|
|
|
else
|
|
|
|
start = ucpi->c_frotor >> 3;
|
|
|
|
|
|
|
|
length = ((uspi->s_fpg + 7) >> 3) - start;
|
2006-06-25 14:47:23 +02:00
|
|
|
loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length,
|
2005-04-17 00:20:36 +02:00
|
|
|
(uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
|
|
|
|
1 << (count - 1 + (uspi->s_fpb & 7)));
|
2006-06-25 14:47:23 +02:00
|
|
|
if (loc == 0) {
|
2005-04-17 00:20:36 +02:00
|
|
|
length = start + 1;
|
2006-06-25 14:47:23 +02:00
|
|
|
loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length,
|
|
|
|
(uspi->s_fpb == 8) ? ufs_fragtable_8fpb :
|
|
|
|
ufs_fragtable_other,
|
|
|
|
1 << (count - 1 + (uspi->s_fpb & 7)));
|
|
|
|
if (loc == 0) {
|
|
|
|
ufs_error(sb, "ufs_bitmap_search",
|
|
|
|
"bitmap corrupted on cg %u, start %u,"
|
|
|
|
" length %u, count %u, freeoff %u\n",
|
|
|
|
ucpi->c_cgx, start, length, count,
|
|
|
|
ucpi->c_freeoff);
|
2007-02-12 09:54:32 +01:00
|
|
|
return INVBLOCK;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
start = 0;
|
|
|
|
}
|
2006-06-25 14:47:23 +02:00
|
|
|
result = (start + length - loc) << 3;
|
2005-04-17 00:20:36 +02:00
|
|
|
ucpi->c_frotor = result;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* found the byte in the map
|
|
|
|
*/
|
2006-06-25 14:47:23 +02:00
|
|
|
|
|
|
|
for (end = result + 8; result < end; result += uspi->s_fpb) {
|
|
|
|
blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result);
|
|
|
|
blockmap <<= 1;
|
|
|
|
mask = mask_arr[count];
|
|
|
|
want = want_arr[count];
|
|
|
|
for (pos = 0; pos <= uspi->s_fpb - count; pos++) {
|
|
|
|
if ((blockmap & mask) == want) {
|
2007-02-12 09:54:32 +01:00
|
|
|
UFSD("EXIT, result %llu\n",
|
|
|
|
(unsigned long long)result);
|
2006-06-25 14:47:23 +02:00
|
|
|
return result + pos;
|
|
|
|
}
|
|
|
|
mask <<= 1;
|
|
|
|
want <<= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n",
|
|
|
|
ucpi->c_cgx);
|
2006-06-25 14:47:24 +02:00
|
|
|
UFSD("EXIT (FAILED)\n");
|
2007-02-12 09:54:32 +01:00
|
|
|
return INVBLOCK;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ufs_clusteracct(struct super_block * sb,
|
|
|
|
struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
|
|
|
|
{
|
|
|
|
struct ufs_sb_private_info * uspi;
|
|
|
|
int i, start, end, forw, back;
|
|
|
|
|
|
|
|
uspi = UFS_SB(sb)->s_uspi;
|
|
|
|
if (uspi->s_contigsumsize <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cnt > 0)
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
|
2005-04-17 00:20:36 +02:00
|
|
|
else
|
2006-06-25 14:47:22 +02:00
|
|
|
ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the size of the cluster going forward.
|
|
|
|
*/
|
|
|
|
start = blkno + 1;
|
|
|
|
end = start + uspi->s_contigsumsize;
|
|
|
|
if ( end >= ucpi->c_nclusterblks)
|
|
|
|
end = ucpi->c_nclusterblks;
|
2006-06-25 14:47:22 +02:00
|
|
|
i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (i > end)
|
|
|
|
i = end;
|
|
|
|
forw = i - start;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the size of the cluster going backward.
|
|
|
|
*/
|
|
|
|
start = blkno - 1;
|
|
|
|
end = start - uspi->s_contigsumsize;
|
|
|
|
if (end < 0 )
|
|
|
|
end = -1;
|
2006-06-25 14:47:22 +02:00
|
|
|
i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end);
|
2005-04-17 00:20:36 +02:00
|
|
|
if ( i < end)
|
|
|
|
i = end;
|
|
|
|
back = start - i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Account for old cluster and the possibly new forward and
|
|
|
|
* back clusters.
|
|
|
|
*/
|
|
|
|
i = back + forw + 1;
|
|
|
|
if (i > uspi->s_contigsumsize)
|
|
|
|
i = uspi->s_contigsumsize;
|
2006-06-25 14:47:22 +02:00
|
|
|
fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (back > 0)
|
2006-06-25 14:47:22 +02:00
|
|
|
fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (forw > 0)
|
2006-06-25 14:47:22 +02:00
|
|
|
fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned char ufs_fragtable_8fpb[] = {
|
|
|
|
0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
|
|
|
|
0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
|
|
|
|
0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
|
|
|
|
0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
|
|
|
|
0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
|
|
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
|
|
|
|
0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
|
|
|
|
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
|
|
|
|
0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
|
|
|
|
0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned char ufs_fragtable_other[] = {
|
|
|
|
0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
|
|
|
|
0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
|
|
|
|
0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
|
|
|
|
0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
|
|
|
|
0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
|
|
|
|
0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
|
|
|
|
0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
|
|
|
|
};
|