ocfs2: Let ocfs2_xa_prepare_entry() do space checks.
ocfs2_xattr_set_in_bucket() doesn't need to do its own hacky space checking. Let's let ocfs2_xa_prepare_entry() (via ocfs2_xa_set()) do the more accurate work. Whenever it doesn't have space, ocfs2_xattr_set_in_bucket() can try to get more space. Signed-off-by: Joel Becker <joel.becker@oracle.com>
This commit is contained in:
Родитель
bca5e9bd1e
Коммит
c5d95df5f7
274
fs/ocfs2/xattr.c
274
fs/ocfs2/xattr.c
|
@ -322,14 +322,6 @@ static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb)
|
|||
return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits);
|
||||
}
|
||||
|
||||
static inline u16 ocfs2_xattr_max_xe_in_bucket(struct super_block *sb)
|
||||
{
|
||||
u16 len = sb->s_blocksize -
|
||||
offsetof(struct ocfs2_xattr_header, xh_entries);
|
||||
|
||||
return len / sizeof(struct ocfs2_xattr_entry);
|
||||
}
|
||||
|
||||
#define bucket_blkno(_b) ((_b)->bu_bhs[0]->b_blocknr)
|
||||
#define bucket_block(_b, _n) ((_b)->bu_bhs[(_n)]->b_data)
|
||||
#define bucket_xh(_b) ((struct ocfs2_xattr_header *)bucket_block((_b), 0))
|
||||
|
@ -5417,42 +5409,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the xattr name/value in the bucket specified in xs.
|
||||
*/
|
||||
static int ocfs2_xattr_set_in_bucket(struct inode *inode,
|
||||
struct ocfs2_xattr_info *xi,
|
||||
struct ocfs2_xattr_search *xs,
|
||||
struct ocfs2_xattr_set_ctxt *ctxt)
|
||||
{
|
||||
int ret;
|
||||
u64 blkno;
|
||||
struct ocfs2_xa_loc loc;
|
||||
|
||||
if (!xs->bucket->bu_bhs[1]) {
|
||||
blkno = bucket_blkno(xs->bucket);
|
||||
ocfs2_xattr_bucket_relse(xs->bucket);
|
||||
ret = ocfs2_read_xattr_bucket(xs->bucket, blkno);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket,
|
||||
xs->not_found ? NULL : xs->here);
|
||||
ret = ocfs2_xa_set(&loc, xi, ctxt);
|
||||
if (ret) {
|
||||
if (ret != -ENOSPC)
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
xs->here = loc.xl_entry;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether the xattr bucket is filled up with the same hash value.
|
||||
* If we want to insert the xattr with the same hash, return -ENOSPC.
|
||||
|
@ -5481,156 +5437,116 @@ static int ocfs2_check_xattr_bucket_collision(struct inode *inode,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to set the entry in the current bucket. If we fail, the caller
|
||||
* will handle getting us another bucket.
|
||||
*/
|
||||
static int ocfs2_xattr_set_entry_bucket(struct inode *inode,
|
||||
struct ocfs2_xattr_info *xi,
|
||||
struct ocfs2_xattr_search *xs,
|
||||
struct ocfs2_xattr_set_ctxt *ctxt)
|
||||
{
|
||||
int ret;
|
||||
struct ocfs2_xa_loc loc;
|
||||
|
||||
mlog_entry("Set xattr %s in xattr bucket\n", xi->xi_name);
|
||||
|
||||
ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket,
|
||||
xs->not_found ? NULL : xs->here);
|
||||
ret = ocfs2_xa_set(&loc, xi, ctxt);
|
||||
if (!ret) {
|
||||
xs->here = loc.xl_entry;
|
||||
goto out;
|
||||
}
|
||||
if (ret != -ENOSPC) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Ok, we need space. Let's try defragmenting the bucket. */
|
||||
ret = ocfs2_defrag_xattr_bucket(inode, ctxt->handle,
|
||||
xs->bucket);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ocfs2_xa_set(&loc, xi, ctxt);
|
||||
if (!ret) {
|
||||
xs->here = loc.xl_entry;
|
||||
goto out;
|
||||
}
|
||||
if (ret != -ENOSPC)
|
||||
mlog_errno(ret);
|
||||
|
||||
|
||||
out:
|
||||
mlog_exit(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
|
||||
struct ocfs2_xattr_info *xi,
|
||||
struct ocfs2_xattr_search *xs,
|
||||
struct ocfs2_xattr_set_ctxt *ctxt)
|
||||
{
|
||||
struct ocfs2_xattr_header *xh;
|
||||
struct ocfs2_xattr_entry *xe;
|
||||
u16 count, header_size, xh_free_start;
|
||||
int free, max_free, need, old;
|
||||
size_t value_size = 0;
|
||||
size_t blocksize = inode->i_sb->s_blocksize;
|
||||
int ret, allocation = 0;
|
||||
int ret;
|
||||
|
||||
mlog_entry("Set xattr %s in xattr index block\n", xi->xi_name);
|
||||
|
||||
try_again:
|
||||
xh = xs->header;
|
||||
count = le16_to_cpu(xh->xh_count);
|
||||
xh_free_start = le16_to_cpu(xh->xh_free_start);
|
||||
header_size = sizeof(struct ocfs2_xattr_header) +
|
||||
count * sizeof(struct ocfs2_xattr_entry);
|
||||
max_free = OCFS2_XATTR_BUCKET_SIZE - header_size -
|
||||
le16_to_cpu(xh->xh_name_value_len) - OCFS2_XATTR_HEADER_GAP;
|
||||
|
||||
mlog_bug_on_msg(header_size > blocksize, "bucket %llu has header size "
|
||||
"of %u which exceed block size\n",
|
||||
(unsigned long long)bucket_blkno(xs->bucket),
|
||||
header_size);
|
||||
|
||||
if (xi->xi_value && xi->xi_value_len > OCFS2_XATTR_INLINE_SIZE)
|
||||
value_size = OCFS2_XATTR_ROOT_SIZE;
|
||||
else if (xi->xi_value)
|
||||
value_size = OCFS2_XATTR_SIZE(xi->xi_value_len);
|
||||
|
||||
if (xs->not_found)
|
||||
need = sizeof(struct ocfs2_xattr_entry) +
|
||||
OCFS2_XATTR_SIZE(xi->xi_name_len) + value_size;
|
||||
else {
|
||||
need = value_size + OCFS2_XATTR_SIZE(xi->xi_name_len);
|
||||
|
||||
/*
|
||||
* We only replace the old value if the new length is smaller
|
||||
* than the old one. Otherwise we will allocate new space in the
|
||||
* bucket to store it.
|
||||
*/
|
||||
xe = xs->here;
|
||||
if (ocfs2_xattr_is_local(xe))
|
||||
old = OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size));
|
||||
else
|
||||
old = OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE);
|
||||
|
||||
if (old >= value_size)
|
||||
need = 0;
|
||||
ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt);
|
||||
if (!ret)
|
||||
goto out;
|
||||
if (ret != -ENOSPC) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
free = xh_free_start - header_size - OCFS2_XATTR_HEADER_GAP;
|
||||
/* Ack, need more space. Let's try to get another bucket! */
|
||||
|
||||
/*
|
||||
* We need to make sure the new name/value pair
|
||||
* can exist in the same block.
|
||||
* We do not allow for overlapping ranges between buckets. And
|
||||
* the maximum number of collisions we will allow for then is
|
||||
* one bucket's worth, so check it here whether we need to
|
||||
* add a new bucket for the insert.
|
||||
*/
|
||||
if (xh_free_start % blocksize < need)
|
||||
free -= xh_free_start % blocksize;
|
||||
|
||||
mlog(0, "xs->not_found = %d, in xattr bucket %llu: free = %d, "
|
||||
"need = %d, max_free = %d, xh_free_start = %u, xh_name_value_len ="
|
||||
" %u\n", xs->not_found,
|
||||
(unsigned long long)bucket_blkno(xs->bucket),
|
||||
free, need, max_free, le16_to_cpu(xh->xh_free_start),
|
||||
le16_to_cpu(xh->xh_name_value_len));
|
||||
|
||||
if (free < need ||
|
||||
(xs->not_found &&
|
||||
count == ocfs2_xattr_max_xe_in_bucket(inode->i_sb))) {
|
||||
if (need <= max_free &&
|
||||
count < ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) {
|
||||
/*
|
||||
* We can create the space by defragment. Since only the
|
||||
* name/value will be moved, the xe shouldn't be changed
|
||||
* in xs.
|
||||
*/
|
||||
ret = ocfs2_defrag_xattr_bucket(inode, ctxt->handle,
|
||||
xs->bucket);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
xh_free_start = le16_to_cpu(xh->xh_free_start);
|
||||
free = xh_free_start - header_size
|
||||
- OCFS2_XATTR_HEADER_GAP;
|
||||
if (xh_free_start % blocksize < need)
|
||||
free -= xh_free_start % blocksize;
|
||||
|
||||
if (free >= need)
|
||||
goto xattr_set;
|
||||
|
||||
mlog(0, "Can't get enough space for xattr insert by "
|
||||
"defragment. Need %u bytes, but we have %d, so "
|
||||
"allocate new bucket for it.\n", need, free);
|
||||
}
|
||||
|
||||
/*
|
||||
* We have to add new buckets or clusters and one
|
||||
* allocation should leave us enough space for insert.
|
||||
*/
|
||||
BUG_ON(allocation);
|
||||
|
||||
/*
|
||||
* We do not allow for overlapping ranges between buckets. And
|
||||
* the maximum number of collisions we will allow for then is
|
||||
* one bucket's worth, so check it here whether we need to
|
||||
* add a new bucket for the insert.
|
||||
*/
|
||||
ret = ocfs2_check_xattr_bucket_collision(inode,
|
||||
xs->bucket,
|
||||
xi->xi_name);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ocfs2_add_new_xattr_bucket(inode,
|
||||
xs->xattr_bh,
|
||||
ret = ocfs2_check_xattr_bucket_collision(inode,
|
||||
xs->bucket,
|
||||
ctxt);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ocfs2_add_new_xattr_bucket() will have updated
|
||||
* xs->bucket if it moved, but it will not have updated
|
||||
* any of the other search fields. Thus, we drop it and
|
||||
* re-search. Everything should be cached, so it'll be
|
||||
* quick.
|
||||
*/
|
||||
ocfs2_xattr_bucket_relse(xs->bucket);
|
||||
ret = ocfs2_xattr_index_block_find(inode, xs->xattr_bh,
|
||||
xi->xi_name_index,
|
||||
xi->xi_name, xs);
|
||||
if (ret && ret != -ENODATA)
|
||||
goto out;
|
||||
xs->not_found = ret;
|
||||
allocation = 1;
|
||||
goto try_again;
|
||||
xi->xi_name);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
xattr_set:
|
||||
ret = ocfs2_xattr_set_in_bucket(inode, xi, xs, ctxt);
|
||||
ret = ocfs2_add_new_xattr_bucket(inode,
|
||||
xs->xattr_bh,
|
||||
xs->bucket,
|
||||
ctxt);
|
||||
if (ret) {
|
||||
mlog_errno(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ocfs2_add_new_xattr_bucket() will have updated
|
||||
* xs->bucket if it moved, but it will not have updated
|
||||
* any of the other search fields. Thus, we drop it and
|
||||
* re-search. Everything should be cached, so it'll be
|
||||
* quick.
|
||||
*/
|
||||
ocfs2_xattr_bucket_relse(xs->bucket);
|
||||
ret = ocfs2_xattr_index_block_find(inode, xs->xattr_bh,
|
||||
xi->xi_name_index,
|
||||
xi->xi_name, xs);
|
||||
if (ret && ret != -ENODATA)
|
||||
goto out;
|
||||
xs->not_found = ret;
|
||||
|
||||
/* Ok, we have a new bucket, let's try again */
|
||||
ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt);
|
||||
if (ret && (ret != -ENOSPC))
|
||||
mlog_errno(ret);
|
||||
|
||||
out:
|
||||
mlog_exit(ret);
|
||||
return ret;
|
||||
|
|
Загрузка…
Ссылка в новой задаче