Commit cfa853e4 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Lachlan McIlroy

[XFS] remove manual lookup from xfs_rename and simplify locking

->rename already gets the target inode passed if it exits. Pass it down to
xfs_rename so that we can avoid looking it up again. Also simplify locking
as the first lock section in xfs_rename can go away now: the isdir is an
invariant over the lifetime of the inode, and new_parent and the nlink
check are namespace topology protected by i_mutex in the VFS. The projid
check needs to move into the second lock section anyway to not be racy.

Also kill the now unused xfs_dir_lookup_int and remove the now-unused
first_locked argumet to xfs_lock_inodes.

SGI-PV: 976035
SGI-Modid: xfs-linux-melb:xfs-kern:30903a
Signed-off-by: default avatarChristoph Hellwig <hch@infradead.org>
Signed-off-by: default avatarLachlan McIlroy <lachlan@sgi.com>
parent 579aa9ca
...@@ -511,7 +511,8 @@ xfs_vn_rename( ...@@ -511,7 +511,8 @@ xfs_vn_rename(
xfs_dentry_to_name(&nname, ndentry); xfs_dentry_to_name(&nname, ndentry);
error = xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), error = xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode),
XFS_I(ndir), &nname); XFS_I(ndir), &nname, new_inode ?
XFS_I(new_inode) : NULL);
if (likely(!error)) { if (likely(!error)) {
if (new_inode) if (new_inode)
xfs_validate_fields(new_inode); xfs_validate_fields(new_inode);
......
...@@ -162,7 +162,7 @@ xfs_swap_extents( ...@@ -162,7 +162,7 @@ xfs_swap_extents(
ips[1] = ip; ips[1] = ip;
} }
xfs_lock_inodes(ips, 2, 0, lock_flags); xfs_lock_inodes(ips, 2, lock_flags);
locked = 1; locked = 1;
/* Verify that both files have the same format */ /* Verify that both files have the same format */
...@@ -265,7 +265,7 @@ xfs_swap_extents( ...@@ -265,7 +265,7 @@ xfs_swap_extents(
locked = 0; locked = 0;
goto error0; goto error0;
} }
xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL); xfs_lock_inodes(ips, 2, XFS_ILOCK_EXCL);
/* /*
* Count the number of extended attribute blocks * Count the number of extended attribute blocks
......
...@@ -524,7 +524,7 @@ int xfs_iflush(xfs_inode_t *, uint); ...@@ -524,7 +524,7 @@ int xfs_iflush(xfs_inode_t *, uint);
void xfs_iflush_all(struct xfs_mount *); void xfs_iflush_all(struct xfs_mount *);
void xfs_ichgtime(xfs_inode_t *, int); void xfs_ichgtime(xfs_inode_t *, int);
xfs_fsize_t xfs_file_last_byte(xfs_inode_t *); xfs_fsize_t xfs_file_last_byte(xfs_inode_t *);
void xfs_lock_inodes(xfs_inode_t **, int, int, uint); void xfs_lock_inodes(xfs_inode_t **, int, uint);
void xfs_synchronize_atime(xfs_inode_t *); void xfs_synchronize_atime(xfs_inode_t *);
void xfs_mark_inode_dirty_sync(xfs_inode_t *); void xfs_mark_inode_dirty_sync(xfs_inode_t *);
......
...@@ -55,85 +55,32 @@ xfs_rename_unlock4( ...@@ -55,85 +55,32 @@ xfs_rename_unlock4(
xfs_iunlock(i_tab[0], lock_mode); xfs_iunlock(i_tab[0], lock_mode);
for (i = 1; i < 4; i++) { for (i = 1; i < 4; i++) {
if (i_tab[i] == NULL) { if (i_tab[i] == NULL)
break; break;
}
/* /*
* Watch out for duplicate entries in the table. * Watch out for duplicate entries in the table.
*/ */
if (i_tab[i] != i_tab[i-1]) { if (i_tab[i] != i_tab[i-1])
xfs_iunlock(i_tab[i], lock_mode); xfs_iunlock(i_tab[i], lock_mode);
}
} }
} }
#ifdef DEBUG
int xfs_rename_skip, xfs_rename_nskip;
#endif
/* /*
* The following routine will acquire the locks required for a rename * Enter all inodes for a rename transaction into a sorted array.
* operation. The code understands the semantics of renames and will
* validate that name1 exists under dp1 & that name2 may or may not
* exist under dp2.
*
* We are renaming dp1/name1 to dp2/name2.
*
* Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success.
*/ */
STATIC int STATIC void
xfs_lock_for_rename( xfs_sort_for_rename(
xfs_inode_t *dp1, /* in: old (source) directory inode */ xfs_inode_t *dp1, /* in: old (source) directory inode */
xfs_inode_t *dp2, /* in: new (target) directory inode */ xfs_inode_t *dp2, /* in: new (target) directory inode */
xfs_inode_t *ip1, /* in: inode of old entry */ xfs_inode_t *ip1, /* in: inode of old entry */
struct xfs_name *name2, /* in: new entry name */ xfs_inode_t *ip2, /* in: inode of new entry, if it
xfs_inode_t **ipp2, /* out: inode of new entry, if it
already exists, NULL otherwise. */ already exists, NULL otherwise. */
xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ xfs_inode_t **i_tab,/* out: array of inode returned, sorted */
int *num_inodes) /* out: number of inodes in array */ int *num_inodes) /* out: number of inodes in array */
{ {
xfs_inode_t *ip2 = NULL;
xfs_inode_t *temp; xfs_inode_t *temp;
xfs_ino_t inum1, inum2;
int error;
int i, j; int i, j;
uint lock_mode;
int diff_dirs = (dp1 != dp2);
/*
* First, find out the current inums of the entries so that we
* can determine the initial locking order. We'll have to
* sanity check stuff after all the locks have been acquired
* to see if we still have the right inodes, directories, etc.
*/
lock_mode = xfs_ilock_map_shared(dp1);
IHOLD(ip1);
xfs_itrace_ref(ip1);
inum1 = ip1->i_ino;
/*
* Unlock dp1 and lock dp2 if they are different.
*/
if (diff_dirs) {
xfs_iunlock_map_shared(dp1, lock_mode);
lock_mode = xfs_ilock_map_shared(dp2);
}
error = xfs_dir_lookup_int(dp2, lock_mode, name2, &inum2, &ip2);
if (error == ENOENT) { /* target does not need to exist. */
inum2 = 0;
} else if (error) {
/*
* If dp2 and dp1 are the same, the next line unlocks dp1.
* Got it?
*/
xfs_iunlock_map_shared(dp2, lock_mode);
IRELE (ip1);
return error;
} else {
xfs_itrace_ref(ip2);
}
/* /*
* i_tab contains a list of pointers to inodes. We initialize * i_tab contains a list of pointers to inodes. We initialize
...@@ -145,21 +92,20 @@ xfs_lock_for_rename( ...@@ -145,21 +92,20 @@ xfs_lock_for_rename(
i_tab[0] = dp1; i_tab[0] = dp1;
i_tab[1] = dp2; i_tab[1] = dp2;
i_tab[2] = ip1; i_tab[2] = ip1;
if (inum2 == 0) { if (ip2) {
*num_inodes = 3;
i_tab[3] = NULL;
} else {
*num_inodes = 4; *num_inodes = 4;
i_tab[3] = ip2; i_tab[3] = ip2;
} else {
*num_inodes = 3;
i_tab[3] = NULL;
} }
*ipp2 = i_tab[3];
/* /*
* Sort the elements via bubble sort. (Remember, there are at * Sort the elements via bubble sort. (Remember, there are at
* most 4 elements to sort, so this is adequate.) * most 4 elements to sort, so this is adequate.)
*/ */
for (i=0; i < *num_inodes; i++) { for (i = 0; i < *num_inodes; i++) {
for (j=1; j < *num_inodes; j++) { for (j = 1; j < *num_inodes; j++) {
if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) {
temp = i_tab[j]; temp = i_tab[j];
i_tab[j] = i_tab[j-1]; i_tab[j] = i_tab[j-1];
...@@ -167,30 +113,6 @@ xfs_lock_for_rename( ...@@ -167,30 +113,6 @@ xfs_lock_for_rename(
} }
} }
} }
/*
* We have dp2 locked. If it isn't first, unlock it.
* If it is first, tell xfs_lock_inodes so it can skip it
* when locking. if dp1 == dp2, xfs_lock_inodes will skip both
* since they are equal. xfs_lock_inodes needs all these inodes
* so that it can unlock and retry if there might be a dead-lock
* potential with the log.
*/
if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) {
#ifdef DEBUG
xfs_rename_skip++;
#endif
xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED);
} else {
#ifdef DEBUG
xfs_rename_nskip++;
#endif
xfs_iunlock_map_shared(dp2, lock_mode);
xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED);
}
return 0;
} }
/* /*
...@@ -202,10 +124,10 @@ xfs_rename( ...@@ -202,10 +124,10 @@ xfs_rename(
struct xfs_name *src_name, struct xfs_name *src_name,
xfs_inode_t *src_ip, xfs_inode_t *src_ip,
xfs_inode_t *target_dp, xfs_inode_t *target_dp,
struct xfs_name *target_name) struct xfs_name *target_name,
xfs_inode_t *target_ip)
{ {
xfs_trans_t *tp; xfs_trans_t *tp = NULL;
xfs_inode_t *target_ip;
xfs_mount_t *mp = src_dp->i_mount; xfs_mount_t *mp = src_dp->i_mount;
int new_parent; /* moving to a new dir */ int new_parent; /* moving to a new dir */
int src_is_directory; /* src_name is a directory */ int src_is_directory; /* src_name is a directory */
...@@ -230,64 +152,31 @@ xfs_rename( ...@@ -230,64 +152,31 @@ xfs_rename(
target_dp, DM_RIGHT_NULL, target_dp, DM_RIGHT_NULL,
src_name->name, target_name->name, src_name->name, target_name->name,
0, 0, 0); 0, 0, 0);
if (error) { if (error)
return error; return error;
}
} }
/* Return through std_return after this point. */ /* Return through std_return after this point. */
/* new_parent = (src_dp != target_dp);
* Lock all the participating inodes. Depending upon whether src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR);
* the target_name exists in the target directory, and
* whether the target directory is the same as the source
* directory, we can lock from 2 to 4 inodes.
* xfs_lock_for_rename() will return ENOENT if src_name
* does not exist in the source directory.
*/
tp = NULL;
error = xfs_lock_for_rename(src_dp, target_dp, src_ip, target_name,
&target_ip, inodes, &num_inodes);
if (error) {
/*
* We have nothing locked, no inode references, and
* no transaction, so just get out.
*/
goto std_return;
}
ASSERT(src_ip != NULL);
if ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR) { if (src_is_directory) {
/* /*
* Check for link count overflow on target_dp * Check for link count overflow on target_dp
*/ */
if (target_ip == NULL && (src_dp != target_dp) && if (target_ip == NULL && new_parent &&
target_dp->i_d.di_nlink >= XFS_MAXLINK) { target_dp->i_d.di_nlink >= XFS_MAXLINK) {
error = XFS_ERROR(EMLINK); error = XFS_ERROR(EMLINK);
xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); goto std_return;
goto rele_return;
} }
} }
/* xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip,
* If we are using project inheritance, we only allow renames inodes, &num_inodes);
* into our tree when the project IDs are the same; else the
* tree quota mechanism would be circumvented.
*/
if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
(target_dp->i_d.di_projid != src_ip->i_d.di_projid))) {
error = XFS_ERROR(EXDEV);
xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED);
goto rele_return;
}
new_parent = (src_dp != target_dp);
src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR);
/* IHOLD(src_ip);
* Drop the locks on our inodes so that we can start the transaction. if (target_ip)
*/ IHOLD(target_ip);
xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED);
XFS_BMAP_INIT(&free_list, &first_block); XFS_BMAP_INIT(&free_list, &first_block);
tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME);
...@@ -314,9 +203,25 @@ xfs_rename( ...@@ -314,9 +203,25 @@ xfs_rename(
} }
/* /*
* Reacquire the inode locks we dropped above. * Lock all the participating inodes. Depending upon whether
* the target_name exists in the target directory, and
* whether the target directory is the same as the source
* directory, we can lock from 2 to 4 inodes.
*/ */
xfs_lock_inodes(inodes, num_inodes, 0, XFS_ILOCK_EXCL); xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);
/*
* If we are using project inheritance, we only allow renames
* into our tree when the project IDs are the same; else the
* tree quota mechanism would be circumvented.
*/
if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) &&
(target_dp->i_d.di_projid != src_ip->i_d.di_projid))) {
error = XFS_ERROR(EXDEV);
xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED);
xfs_trans_cancel(tp, cancel_flags);
goto rele_return;
}
/* /*
* Join all the inodes to the transaction. From this point on, * Join all the inodes to the transaction. From this point on,
......
...@@ -41,49 +41,6 @@ ...@@ -41,49 +41,6 @@
#include "xfs_utils.h" #include "xfs_utils.h"
int
xfs_dir_lookup_int(
xfs_inode_t *dp,
uint lock_mode,
struct xfs_name *name,
xfs_ino_t *inum,
xfs_inode_t **ipp)
{
int error;
xfs_itrace_entry(dp);
error = xfs_dir_lookup(NULL, dp, name, inum);
if (!error) {
/*
* Unlock the directory. We do this because we can't
* hold the directory lock while doing the vn_get()
* in xfs_iget(). Doing so could cause us to hold
* a lock while waiting for the inode to finish
* being inactive while it's waiting for a log
* reservation in the inactive routine.
*/
xfs_iunlock(dp, lock_mode);
error = xfs_iget(dp->i_mount, NULL, *inum, 0, 0, ipp, 0);
xfs_ilock(dp, lock_mode);
if (error) {
*ipp = NULL;
} else if ((*ipp)->i_d.di_mode == 0) {
/*
* The inode has been freed. Something is
* wrong so just get out of here.
*/
xfs_iunlock(dp, lock_mode);
xfs_iput_new(*ipp, 0);
*ipp = NULL;
xfs_ilock(dp, lock_mode);
error = XFS_ERROR(ENOENT);
}
}
return error;
}
/* /*
* Allocates a new inode from disk and return a pointer to the * Allocates a new inode from disk and return a pointer to the
* incore copy. This routine will internally commit the current * incore copy. This routine will internally commit the current
......
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
#define IRELE(ip) VN_RELE(XFS_ITOV(ip)) #define IRELE(ip) VN_RELE(XFS_ITOV(ip))
#define IHOLD(ip) VN_HOLD(XFS_ITOV(ip)) #define IHOLD(ip) VN_HOLD(XFS_ITOV(ip))
extern int xfs_dir_lookup_int(xfs_inode_t *, uint, struct xfs_name *,
xfs_ino_t *, xfs_inode_t **);
extern int xfs_truncate_file(xfs_mount_t *, xfs_inode_t *); extern int xfs_truncate_file(xfs_mount_t *, xfs_inode_t *);
extern int xfs_dir_ialloc(xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t, extern int xfs_dir_ialloc(xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t,
xfs_dev_t, cred_t *, prid_t, int, xfs_dev_t, cred_t *, prid_t, int,
......
...@@ -1982,7 +1982,7 @@ xfs_lock_dir_and_entry( ...@@ -1982,7 +1982,7 @@ xfs_lock_dir_and_entry(
ips[0] = ip; ips[0] = ip;
ips[1] = dp; ips[1] = dp;
xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL); xfs_lock_inodes(ips, 2, XFS_ILOCK_EXCL);
} }
/* else e_inum == dp->i_ino */ /* else e_inum == dp->i_ino */
/* This can happen if we're asked to lock /x/.. /* This can happen if we're asked to lock /x/..
...@@ -2030,7 +2030,6 @@ void ...@@ -2030,7 +2030,6 @@ void
xfs_lock_inodes( xfs_lock_inodes(
xfs_inode_t **ips, xfs_inode_t **ips,
int inodes, int inodes,
int first_locked,
uint lock_mode) uint lock_mode)
{ {
int attempts = 0, i, j, try_lock; int attempts = 0, i, j, try_lock;
...@@ -2038,13 +2037,8 @@ xfs_lock_inodes( ...@@ -2038,13 +2037,8 @@ xfs_lock_inodes(
ASSERT(ips && (inodes >= 2)); /* we need at least two */ ASSERT(ips && (inodes >= 2)); /* we need at least two */
if (first_locked) { try_lock = 0;
try_lock = 1; i = 0;
i = 1;
} else {
try_lock = 0;
i = 0;
}
again: again:
for (; i < inodes; i++) { for (; i < inodes; i++) {
...@@ -2406,7 +2400,7 @@ xfs_link( ...@@ -2406,7 +2400,7 @@ xfs_link(
ips[1] = sip; ips[1] = sip;
} }
xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL); xfs_lock_inodes(ips, 2, XFS_ILOCK_EXCL);
/* /*
* Increment vnode ref counts since xfs_trans_commit & * Increment vnode ref counts since xfs_trans_commit &
......
...@@ -47,7 +47,7 @@ int xfs_change_file_space(struct xfs_inode *ip, int cmd, ...@@ -47,7 +47,7 @@ int xfs_change_file_space(struct xfs_inode *ip, int cmd,
struct cred *credp, int attr_flags); struct cred *credp, int attr_flags);
int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name, int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
struct xfs_inode *src_ip, struct xfs_inode *target_dp, struct xfs_inode *src_ip, struct xfs_inode *target_dp,
struct xfs_name *target_name); struct xfs_name *target_name, struct xfs_inode *target_ip);
int xfs_attr_get(struct xfs_inode *ip, const char *name, char *value, int xfs_attr_get(struct xfs_inode *ip, const char *name, char *value,
int *valuelenp, int flags, cred_t *cred); int *valuelenp, int flags, cred_t *cred);
int xfs_attr_set(struct xfs_inode *dp, const char *name, char *value, int xfs_attr_set(struct xfs_inode *dp, const char *name, char *value,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment