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:
parent
bca5e9bd1e
commit
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);
|
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_blkno(_b) ((_b)->bu_bhs[0]->b_blocknr)
|
||||||
#define bucket_block(_b, _n) ((_b)->bu_bhs[(_n)]->b_data)
|
#define bucket_block(_b, _n) ((_b)->bu_bhs[(_n)]->b_data)
|
||||||
#define bucket_xh(_b) ((struct ocfs2_xattr_header *)bucket_block((_b), 0))
|
#define bucket_xh(_b) ((struct ocfs2_xattr_header *)bucket_block((_b), 0))
|
||||||
|
@ -5417,42 +5409,6 @@ out:
|
||||||
return ret;
|
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.
|
* 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.
|
* 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;
|
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,
|
static int ocfs2_xattr_set_entry_index_block(struct inode *inode,
|
||||||
struct ocfs2_xattr_info *xi,
|
struct ocfs2_xattr_info *xi,
|
||||||
struct ocfs2_xattr_search *xs,
|
struct ocfs2_xattr_search *xs,
|
||||||
struct ocfs2_xattr_set_ctxt *ctxt)
|
struct ocfs2_xattr_set_ctxt *ctxt)
|
||||||
{
|
{
|
||||||
struct ocfs2_xattr_header *xh;
|
int ret;
|
||||||
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;
|
|
||||||
|
|
||||||
mlog_entry("Set xattr %s in xattr index block\n", xi->xi_name);
|
mlog_entry("Set xattr %s in xattr index block\n", xi->xi_name);
|
||||||
|
|
||||||
try_again:
|
ret = ocfs2_xattr_set_entry_bucket(inode, xi, xs, ctxt);
|
||||||
xh = xs->header;
|
if (!ret)
|
||||||
count = le16_to_cpu(xh->xh_count);
|
goto out;
|
||||||
xh_free_start = le16_to_cpu(xh->xh_free_start);
|
if (ret != -ENOSPC) {
|
||||||
header_size = sizeof(struct ocfs2_xattr_header) +
|
mlog_errno(ret);
|
||||||
count * sizeof(struct ocfs2_xattr_entry);
|
goto out;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* We do not allow for overlapping ranges between buckets. And
|
||||||
* can exist in the same block.
|
* 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)
|
ret = ocfs2_check_xattr_bucket_collision(inode,
|
||||||
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,
|
|
||||||
xs->bucket,
|
xs->bucket,
|
||||||
ctxt);
|
xi->xi_name);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
mlog_errno(ret);
|
mlog_errno(ret);
|
||||||
goto out;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xattr_set:
|
ret = ocfs2_add_new_xattr_bucket(inode,
|
||||||
ret = ocfs2_xattr_set_in_bucket(inode, xi, xs, ctxt);
|
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:
|
out:
|
||||||
mlog_exit(ret);
|
mlog_exit(ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue