Commit 8dac6bee authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6:
  AFS: Use i_generation not i_version for the vnode uniquifier
  AFS: Set s_id in the superblock to the volume name
  vfs: Fix data corruption after failed write in __block_write_begin()
  afs: afs_fill_page reads too much, or wrong data
  VFS: Fix vfsmount overput on simultaneous automount
  fix wrong iput on d_inode introduced by e6bc45d6
  Delay struct net freeing while there's a sysfs instance refering to it
  afs: fix sget() races, close leak on umount
  ubifs: fix sget races
  ubifs: split allocation of ubifs_info into a separate function
  fix leak in proc_set_super()
parents f8f44f09 d6e43f75
...@@ -584,11 +584,11 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -584,11 +584,11 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
success: success:
d_add(dentry, inode); d_add(dentry, inode);
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%llu }", _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }",
fid.vnode, fid.vnode,
fid.unique, fid.unique,
dentry->d_inode->i_ino, dentry->d_inode->i_ino,
(unsigned long long)dentry->d_inode->i_version); dentry->d_inode->i_generation);
return NULL; return NULL;
} }
...@@ -671,10 +671,10 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -671,10 +671,10 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
* been deleted and replaced, and the original vnode ID has * been deleted and replaced, and the original vnode ID has
* been reused */ * been reused */
if (fid.unique != vnode->fid.unique) { if (fid.unique != vnode->fid.unique) {
_debug("%s: file deleted (uq %u -> %u I:%llu)", _debug("%s: file deleted (uq %u -> %u I:%u)",
dentry->d_name.name, fid.unique, dentry->d_name.name, fid.unique,
vnode->fid.unique, vnode->fid.unique,
(unsigned long long)dentry->d_inode->i_version); dentry->d_inode->i_generation);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
set_bit(AFS_VNODE_DELETED, &vnode->flags); set_bit(AFS_VNODE_DELETED, &vnode->flags);
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
......
...@@ -89,7 +89,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -89,7 +89,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
i_size_write(&vnode->vfs_inode, size); i_size_write(&vnode->vfs_inode, size);
vnode->vfs_inode.i_uid = status->owner; vnode->vfs_inode.i_uid = status->owner;
vnode->vfs_inode.i_gid = status->group; vnode->vfs_inode.i_gid = status->group;
vnode->vfs_inode.i_version = vnode->fid.unique; vnode->vfs_inode.i_generation = vnode->fid.unique;
vnode->vfs_inode.i_nlink = status->nlink; vnode->vfs_inode.i_nlink = status->nlink;
mode = vnode->vfs_inode.i_mode; mode = vnode->vfs_inode.i_mode;
...@@ -102,6 +102,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, ...@@ -102,6 +102,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server; vnode->vfs_inode.i_ctime.tv_sec = status->mtime_server;
vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime;
vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime;
vnode->vfs_inode.i_version = data_version;
} }
expected_version = status->data_version; expected_version = status->data_version;
......
...@@ -75,7 +75,8 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) ...@@ -75,7 +75,8 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
inode->i_ctime.tv_nsec = 0; inode->i_ctime.tv_nsec = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime; inode->i_atime = inode->i_mtime = inode->i_ctime;
inode->i_blocks = 0; inode->i_blocks = 0;
inode->i_version = vnode->fid.unique; inode->i_generation = vnode->fid.unique;
inode->i_version = vnode->status.data_version;
inode->i_mapping->a_ops = &afs_fs_aops; inode->i_mapping->a_ops = &afs_fs_aops;
/* check to see whether a symbolic link is really a mountpoint */ /* check to see whether a symbolic link is really a mountpoint */
...@@ -100,7 +101,7 @@ static int afs_iget5_test(struct inode *inode, void *opaque) ...@@ -100,7 +101,7 @@ static int afs_iget5_test(struct inode *inode, void *opaque)
struct afs_iget_data *data = opaque; struct afs_iget_data *data = opaque;
return inode->i_ino == data->fid.vnode && return inode->i_ino == data->fid.vnode &&
inode->i_version == data->fid.unique; inode->i_generation == data->fid.unique;
} }
/* /*
...@@ -122,7 +123,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque) ...@@ -122,7 +123,7 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_vnode *vnode = AFS_FS_I(inode);
inode->i_ino = data->fid.vnode; inode->i_ino = data->fid.vnode;
inode->i_version = data->fid.unique; inode->i_generation = data->fid.unique;
vnode->fid = data->fid; vnode->fid = data->fid;
vnode->volume = data->volume; vnode->volume = data->volume;
...@@ -380,8 +381,7 @@ int afs_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -380,8 +381,7 @@ int afs_getattr(struct vfsmount *mnt, struct dentry *dentry,
inode = dentry->d_inode; inode = dentry->d_inode;
_enter("{ ino=%lu v=%llu }", inode->i_ino, _enter("{ ino=%lu v=%u }", inode->i_ino, inode->i_generation);
(unsigned long long)inode->i_version);
generic_fillattr(inode, stat); generic_fillattr(inode, stat);
return 0; return 0;
......
...@@ -31,8 +31,8 @@ ...@@ -31,8 +31,8 @@
static void afs_i_init_once(void *foo); static void afs_i_init_once(void *foo);
static struct dentry *afs_mount(struct file_system_type *fs_type, static struct dentry *afs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data); int flags, const char *dev_name, void *data);
static void afs_kill_super(struct super_block *sb);
static struct inode *afs_alloc_inode(struct super_block *sb); static struct inode *afs_alloc_inode(struct super_block *sb);
static void afs_put_super(struct super_block *sb);
static void afs_destroy_inode(struct inode *inode); static void afs_destroy_inode(struct inode *inode);
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf); static int afs_statfs(struct dentry *dentry, struct kstatfs *buf);
...@@ -40,7 +40,7 @@ struct file_system_type afs_fs_type = { ...@@ -40,7 +40,7 @@ struct file_system_type afs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "afs", .name = "afs",
.mount = afs_mount, .mount = afs_mount,
.kill_sb = kill_anon_super, .kill_sb = afs_kill_super,
.fs_flags = 0, .fs_flags = 0,
}; };
...@@ -50,7 +50,6 @@ static const struct super_operations afs_super_ops = { ...@@ -50,7 +50,6 @@ static const struct super_operations afs_super_ops = {
.drop_inode = afs_drop_inode, .drop_inode = afs_drop_inode,
.destroy_inode = afs_destroy_inode, .destroy_inode = afs_destroy_inode,
.evict_inode = afs_evict_inode, .evict_inode = afs_evict_inode,
.put_super = afs_put_super,
.show_options = generic_show_options, .show_options = generic_show_options,
}; };
...@@ -282,19 +281,25 @@ static int afs_parse_device_name(struct afs_mount_params *params, ...@@ -282,19 +281,25 @@ static int afs_parse_device_name(struct afs_mount_params *params,
*/ */
static int afs_test_super(struct super_block *sb, void *data) static int afs_test_super(struct super_block *sb, void *data)
{ {
struct afs_mount_params *params = data; struct afs_super_info *as1 = data;
struct afs_super_info *as = sb->s_fs_info; struct afs_super_info *as = sb->s_fs_info;
return as->volume == params->volume; return as->volume == as1->volume;
}
static int afs_set_super(struct super_block *sb, void *data)
{
sb->s_fs_info = data;
return set_anon_super(sb, NULL);
} }
/* /*
* fill in the superblock * fill in the superblock
*/ */
static int afs_fill_super(struct super_block *sb, void *data) static int afs_fill_super(struct super_block *sb,
struct afs_mount_params *params)
{ {
struct afs_mount_params *params = data; struct afs_super_info *as = sb->s_fs_info;
struct afs_super_info *as = NULL;
struct afs_fid fid; struct afs_fid fid;
struct dentry *root = NULL; struct dentry *root = NULL;
struct inode *inode = NULL; struct inode *inode = NULL;
...@@ -302,23 +307,13 @@ static int afs_fill_super(struct super_block *sb, void *data) ...@@ -302,23 +307,13 @@ static int afs_fill_super(struct super_block *sb, void *data)
_enter(""); _enter("");
/* allocate a superblock info record */
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (!as) {
_leave(" = -ENOMEM");
return -ENOMEM;
}
afs_get_volume(params->volume);
as->volume = params->volume;
/* fill in the superblock */ /* fill in the superblock */
sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = AFS_FS_MAGIC; sb->s_magic = AFS_FS_MAGIC;
sb->s_op = &afs_super_ops; sb->s_op = &afs_super_ops;
sb->s_fs_info = as;
sb->s_bdi = &as->volume->bdi; sb->s_bdi = &as->volume->bdi;
strlcpy(sb->s_id, as->volume->vlocation->vldb.name, sizeof(sb->s_id));
/* allocate the root inode and dentry */ /* allocate the root inode and dentry */
fid.vid = as->volume->vid; fid.vid = as->volume->vid;
...@@ -326,7 +321,7 @@ static int afs_fill_super(struct super_block *sb, void *data) ...@@ -326,7 +321,7 @@ static int afs_fill_super(struct super_block *sb, void *data)
fid.unique = 1; fid.unique = 1;
inode = afs_iget(sb, params->key, &fid, NULL, NULL); inode = afs_iget(sb, params->key, &fid, NULL, NULL);
if (IS_ERR(inode)) if (IS_ERR(inode))
goto error_inode; return PTR_ERR(inode);
if (params->autocell) if (params->autocell)
set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags);
...@@ -342,16 +337,8 @@ static int afs_fill_super(struct super_block *sb, void *data) ...@@ -342,16 +337,8 @@ static int afs_fill_super(struct super_block *sb, void *data)
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
error_inode:
ret = PTR_ERR(inode);
inode = NULL;
error: error:
iput(inode); iput(inode);
afs_put_volume(as->volume);
kfree(as);
sb->s_fs_info = NULL;
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
...@@ -367,6 +354,7 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -367,6 +354,7 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
struct afs_volume *vol; struct afs_volume *vol;
struct key *key; struct key *key;
char *new_opts = kstrdup(options, GFP_KERNEL); char *new_opts = kstrdup(options, GFP_KERNEL);
struct afs_super_info *as;
int ret; int ret;
_enter(",,%s,%p", dev_name, options); _enter(",,%s,%p", dev_name, options);
...@@ -399,12 +387,22 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -399,12 +387,22 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
ret = PTR_ERR(vol); ret = PTR_ERR(vol);
goto error; goto error;
} }
params.volume = vol;
/* allocate a superblock info record */
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (!as) {
ret = -ENOMEM;
afs_put_volume(vol);
goto error;
}
as->volume = vol;
/* allocate a deviceless superblock */ /* allocate a deviceless superblock */
sb = sget(fs_type, afs_test_super, set_anon_super, &params); sb = sget(fs_type, afs_test_super, afs_set_super, as);
if (IS_ERR(sb)) { if (IS_ERR(sb)) {
ret = PTR_ERR(sb); ret = PTR_ERR(sb);
afs_put_volume(vol);
kfree(as);
goto error; goto error;
} }
...@@ -422,16 +420,16 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -422,16 +420,16 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
} else { } else {
_debug("reuse"); _debug("reuse");
ASSERTCMP(sb->s_flags, &, MS_ACTIVE); ASSERTCMP(sb->s_flags, &, MS_ACTIVE);
afs_put_volume(vol);
kfree(as);
} }
afs_put_volume(params.volume);
afs_put_cell(params.cell); afs_put_cell(params.cell);
kfree(new_opts); kfree(new_opts);
_leave(" = 0 [%p]", sb); _leave(" = 0 [%p]", sb);
return dget(sb->s_root); return dget(sb->s_root);
error: error:
afs_put_volume(params.volume);
afs_put_cell(params.cell); afs_put_cell(params.cell);
key_put(params.key); key_put(params.key);
kfree(new_opts); kfree(new_opts);
...@@ -439,18 +437,12 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -439,18 +437,12 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
/* static void afs_kill_super(struct super_block *sb)
* finish the unmounting process on the superblock
*/
static void afs_put_super(struct super_block *sb)
{ {
struct afs_super_info *as = sb->s_fs_info; struct afs_super_info *as = sb->s_fs_info;
kill_anon_super(sb);
_enter("");
afs_put_volume(as->volume); afs_put_volume(as->volume);
kfree(as);
_leave("");
} }
/* /*
......
...@@ -84,23 +84,21 @@ void afs_put_writeback(struct afs_writeback *wb) ...@@ -84,23 +84,21 @@ void afs_put_writeback(struct afs_writeback *wb)
* partly or wholly fill a page that's under preparation for writing * partly or wholly fill a page that's under preparation for writing
*/ */
static int afs_fill_page(struct afs_vnode *vnode, struct key *key, static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
loff_t pos, unsigned len, struct page *page) loff_t pos, struct page *page)
{ {
loff_t i_size; loff_t i_size;
unsigned eof;
int ret; int ret;
int len;
_enter(",,%llu,%u", (unsigned long long)pos, len); _enter(",,%llu", (unsigned long long)pos);
ASSERTCMP(len, <=, PAGE_CACHE_SIZE);
i_size = i_size_read(&vnode->vfs_inode); i_size = i_size_read(&vnode->vfs_inode);
if (pos + len > i_size) if (pos + PAGE_CACHE_SIZE > i_size)
eof = i_size; len = i_size - pos;
else else
eof = PAGE_CACHE_SIZE; len = PAGE_CACHE_SIZE;
ret = afs_vnode_fetch_data(vnode, key, 0, eof, page); ret = afs_vnode_fetch_data(vnode, key, pos, len, page);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOENT) { if (ret == -ENOENT) {
_debug("got NOENT from server" _debug("got NOENT from server"
...@@ -153,9 +151,8 @@ int afs_write_begin(struct file *file, struct address_space *mapping, ...@@ -153,9 +151,8 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
*pagep = page; *pagep = page;
/* page won't leak in error case: it eventually gets cleaned off LRU */ /* page won't leak in error case: it eventually gets cleaned off LRU */
if (!PageUptodate(page)) { if (!PageUptodate(page) && len != PAGE_CACHE_SIZE) {
_debug("not up to date"); ret = afs_fill_page(vnode, key, index << PAGE_CACHE_SHIFT, page);
ret = afs_fill_page(vnode, key, pos, len, page);
if (ret < 0) { if (ret < 0) {
kfree(candidate); kfree(candidate);
_leave(" = %d [prep]", ret); _leave(" = %d [prep]", ret);
......
...@@ -1902,10 +1902,8 @@ int __block_write_begin(struct page *page, loff_t pos, unsigned len, ...@@ -1902,10 +1902,8 @@ int __block_write_begin(struct page *page, loff_t pos, unsigned len,
if (!buffer_uptodate(*wait_bh)) if (!buffer_uptodate(*wait_bh))
err = -EIO; err = -EIO;
} }
if (unlikely(err)) { if (unlikely(err))
page_zero_new_buffers(page, from, to); page_zero_new_buffers(page, from, to);
ClearPageUptodate(page);
}
return err; return err;
} }
EXPORT_SYMBOL(__block_write_begin); EXPORT_SYMBOL(__block_write_begin);
......
...@@ -812,6 +812,11 @@ static int follow_automount(struct path *path, unsigned flags, ...@@ -812,6 +812,11 @@ static int follow_automount(struct path *path, unsigned flags,
if (!mnt) /* mount collision */ if (!mnt) /* mount collision */
return 0; return 0;
if (!*need_mntput) {
/* lock_mount() may release path->mnt on error */
mntget(path->mnt);
*need_mntput = true;
}
err = finish_automount(mnt, path); err = finish_automount(mnt, path);
switch (err) { switch (err) {
...@@ -819,12 +824,9 @@ static int follow_automount(struct path *path, unsigned flags, ...@@ -819,12 +824,9 @@ static int follow_automount(struct path *path, unsigned flags,
/* Someone else made a mount here whilst we were busy */ /* Someone else made a mount here whilst we were busy */
return 0; return 0;
case 0: case 0:
dput(path->dentry); path_put(path);
if (*need_mntput)
mntput(path->mnt);
path->mnt = mnt; path->mnt = mnt;
path->dentry = dget(mnt->mnt_root); path->dentry = dget(mnt->mnt_root);
*need_mntput = true;
return 0; return 0;
default: default:
return err; return err;
...@@ -844,9 +846,10 @@ static int follow_automount(struct path *path, unsigned flags, ...@@ -844,9 +846,10 @@ static int follow_automount(struct path *path, unsigned flags,
*/ */
static int follow_managed(struct path *path, unsigned flags) static int follow_managed(struct path *path, unsigned flags)
{ {
struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
unsigned managed; unsigned managed;
bool need_mntput = false; bool need_mntput = false;
int ret; int ret = 0;
/* Given that we're not holding a lock here, we retain the value in a /* Given that we're not holding a lock here, we retain the value in a
* local variable for each dentry as we look at it so that we don't see * local variable for each dentry as we look at it so that we don't see
...@@ -861,7 +864,7 @@ static int follow_managed(struct path *path, unsigned flags) ...@@ -861,7 +864,7 @@ static int follow_managed(struct path *path, unsigned flags)
BUG_ON(!path->dentry->d_op->d_manage); BUG_ON(!path->dentry->d_op->d_manage);
ret = path->dentry->d_op->d_manage(path->dentry, false); ret = path->dentry->d_op->d_manage(path->dentry, false);
if (ret < 0) if (ret < 0)
return ret == -EISDIR ? 0 : ret; break;
} }
/* Transit to a mounted filesystem. */ /* Transit to a mounted filesystem. */
...@@ -887,14 +890,19 @@ static int follow_managed(struct path *path, unsigned flags) ...@@ -887,14 +890,19 @@ static int follow_managed(struct path *path, unsigned flags)
if (managed & DCACHE_NEED_AUTOMOUNT) { if (managed & DCACHE_NEED_AUTOMOUNT) {
ret = follow_automount(path, flags, &need_mntput); ret = follow_automount(path, flags, &need_mntput);
if (ret < 0) if (ret < 0)
return ret == -EISDIR ? 0 : ret; break;
continue; continue;
} }
/* We didn't change the current path point */ /* We didn't change the current path point */
break; break;
} }
return 0;
if (need_mntput && path->mnt == mnt)
mntput(path->mnt);
if (ret == -EISDIR)
ret = 0;
return ret;
} }
int follow_down_one(struct path *path) int follow_down_one(struct path *path)
...@@ -2713,8 +2721,10 @@ static long do_unlinkat(int dfd, const char __user *pathname) ...@@ -2713,8 +2721,10 @@ static long do_unlinkat(int dfd, const char __user *pathname)
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
/* Why not before? Because we want correct error value */ /* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len])
goto slashes;
inode = dentry->d_inode; inode = dentry->d_inode;
if (nd.last.name[nd.last.len] || !inode) if (!inode)
goto slashes; goto slashes;
ihold(inode); ihold(inode);
error = mnt_want_write(nd.path.mnt); error = mnt_want_write(nd.path.mnt);
......
...@@ -28,11 +28,12 @@ static int proc_test_super(struct super_block *sb, void *data) ...@@ -28,11 +28,12 @@ static int proc_test_super(struct super_block *sb, void *data)
static int proc_set_super(struct super_block *sb, void *data) static int proc_set_super(struct super_block *sb, void *data)
{ {
struct pid_namespace *ns; int err = set_anon_super(sb, NULL);
if (!err) {
ns = (struct pid_namespace *)data; struct pid_namespace *ns = (struct pid_namespace *)data;
sb->s_fs_info = get_pid_ns(ns); sb->s_fs_info = get_pid_ns(ns);
return set_anon_super(sb, NULL); }
return err;
} }
static struct dentry *proc_mount(struct file_system_type *fs_type, static struct dentry *proc_mount(struct file_system_type *fs_type,
......
...@@ -95,6 +95,14 @@ static int sysfs_set_super(struct super_block *sb, void *data) ...@@ -95,6 +95,14 @@ static int sysfs_set_super(struct super_block *sb, void *data)
return error; return error;
} }
static void free_sysfs_super_info(struct sysfs_super_info *info)
{
int type;
for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
kobj_ns_drop(type, info->ns[type]);
kfree(info);
}
static struct dentry *sysfs_mount(struct file_system_type *fs_type, static struct dentry *sysfs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
{ {
...@@ -108,11 +116,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, ...@@ -108,11 +116,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
info->ns[type] = kobj_ns_current(type); info->ns[type] = kobj_ns_grab_current(type);
sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
if (IS_ERR(sb) || sb->s_fs_info != info) if (IS_ERR(sb) || sb->s_fs_info != info)
kfree(info); free_sysfs_super_info(info);
if (IS_ERR(sb)) if (IS_ERR(sb))
return ERR_CAST(sb); return ERR_CAST(sb);
if (!sb->s_root) { if (!sb->s_root) {
...@@ -131,12 +139,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, ...@@ -131,12 +139,11 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
static void sysfs_kill_sb(struct super_block *sb) static void sysfs_kill_sb(struct super_block *sb)
{ {
struct sysfs_super_info *info = sysfs_info(sb); struct sysfs_super_info *info = sysfs_info(sb);
/* Remove the superblock from fs_supers/s_instances /* Remove the superblock from fs_supers/s_instances
* so we can't find it, before freeing sysfs_super_info. * so we can't find it, before freeing sysfs_super_info.
*/ */
kill_anon_super(sb); kill_anon_super(sb);
kfree(info); free_sysfs_super_info(info);
} }
static struct file_system_type sysfs_fs_type = { static struct file_system_type sysfs_fs_type = {
...@@ -145,28 +152,6 @@ static struct file_system_type sysfs_fs_type = { ...@@ -145,28 +152,6 @@ static struct file_system_type sysfs_fs_type = {
.kill_sb = sysfs_kill_sb, .kill_sb = sysfs_kill_sb,
}; };
void sysfs_exit_ns(enum kobj_ns_type type, const void *ns)
{
struct super_block *sb;
mutex_lock(&sysfs_mutex);
spin_lock(&sb_lock);
list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
struct sysfs_super_info *info = sysfs_info(sb);
/*
* If we see a superblock on the fs_supers/s_instances
* list the unmount has not completed and sb->s_fs_info
* points to a valid struct sysfs_super_info.
*/
/* Ignore superblocks with the wrong ns */
if (info->ns[type] != ns)
continue;
info->ns[type] = NULL;
}
spin_unlock(&sb_lock);
mutex_unlock(&sysfs_mutex);
}
int __init sysfs_init(void) int __init sysfs_init(void)
{ {
int err = -ENOMEM; int err = -ENOMEM;
......
...@@ -136,7 +136,7 @@ struct sysfs_addrm_cxt { ...@@ -136,7 +136,7 @@ struct sysfs_addrm_cxt {
* instance). * instance).
*/ */
struct sysfs_super_info { struct sysfs_super_info {
const void *ns[KOBJ_NS_TYPES]; void *ns[KOBJ_NS_TYPES];
}; };
#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
extern struct sysfs_dirent sysfs_root; extern struct sysfs_dirent sysfs_root;
......
...@@ -1848,7 +1848,6 @@ static void ubifs_put_super(struct super_block *sb) ...@@ -1848,7 +1848,6 @@ static void ubifs_put_super(struct super_block *sb)
bdi_destroy(&c->bdi); bdi_destroy(&c->bdi);
ubi_close_volume(c->ubi); ubi_close_volume(c->ubi);
mutex_unlock(&c->umount_mutex); mutex_unlock(&c->umount_mutex);
kfree(c);
} }
static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data)
...@@ -1971,17 +1970,12 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode) ...@@ -1971,17 +1970,12 @@ static struct ubi_volume_desc *open_ubi(const char *name, int mode)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
static int ubifs_fill_super(struct super_block *sb, void *data, int silent) static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi)
{ {
struct ubi_volume_desc *ubi = sb->s_fs_info;
struct ubifs_info *c; struct ubifs_info *c;
struct inode *root;
int err;
c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL); c = kzalloc(sizeof(struct ubifs_info), GFP_KERNEL);
if (!c) if (c) {
return -ENOMEM;
spin_lock_init(&c->cnt_lock); spin_lock_init(&c->cnt_lock);
spin_lock_init(&c->cs_lock); spin_lock_init(&c->cs_lock);
spin_lock_init(&c->buds_lock); spin_lock_init(&c->buds_lock);
...@@ -2014,18 +2008,27 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2014,18 +2008,27 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
INIT_LIST_HEAD(&c->orph_new); INIT_LIST_HEAD(&c->orph_new);
c->no_chk_data_crc = 1; c->no_chk_data_crc = 1;
c->vfs_sb = sb;
c->highest_inum = UBIFS_FIRST_INO; c->highest_inum = UBIFS_FIRST_INO;
c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM; c->lhead_lnum = c->ltail_lnum = UBIFS_LOG_LNUM;
ubi_get_volume_info(ubi, &c->vi); ubi_get_volume_info(ubi, &c->vi);
ubi_get_device_info(c->vi.ubi_num, &c->di); ubi_get_device_info(c->vi.ubi_num, &c->di);
}
return c;
}
static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
{
struct ubifs_info *c = sb->s_fs_info;
struct inode *root;
int err;
c->vfs_sb = sb;
/* Re-open the UBI device in read-write mode */ /* Re-open the UBI device in read-write mode */
c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE); c->ubi = ubi_open_volume(c->vi.ubi_num, c->vi.vol_id, UBI_READWRITE);
if (IS_ERR(c->ubi)) { if (IS_ERR(c->ubi)) {
err = PTR_ERR(c->ubi); err = PTR_ERR(c->ubi);
goto out_free; goto out;
} }
/* /*
...@@ -2091,24 +2094,29 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -2091,24 +2094,29 @@ static int ubifs_fill_super(struct super_block *sb, void *data, int silent)
bdi_destroy(&c->bdi); bdi_destroy(&c->bdi);
out_close: out_close:
ubi_close_volume(c->ubi); ubi_close_volume(c->ubi);
out_free: out:
kfree(c);
return err; return err;
} }
static int sb_test(struct super_block *sb, void *data) static int sb_test(struct super_block *sb, void *data)
{ {
dev_t *dev = data; struct ubifs_info *c1 = data;
struct ubifs_info *c = sb->s_fs_info; struct ubifs_info *c = sb->s_fs_info;
return c->vi.cdev == *dev; return c->vi.cdev == c1->vi.cdev;
}
static int sb_set(struct super_block *sb, void *data)
{
sb->s_fs_info = data;
return set_anon_super(sb, NULL);
} }
static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
const char *name, void *data) const char *name, void *data)
{ {
struct ubi_volume_desc *ubi; struct ubi_volume_desc *ubi;
struct ubi_volume_info vi; struct ubifs_info *c;
struct super_block *sb; struct super_block *sb;
int err; int err;
...@@ -2125,19 +2133,24 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, ...@@ -2125,19 +2133,24 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
name, (int)PTR_ERR(ubi)); name, (int)PTR_ERR(ubi));
return ERR_CAST(ubi); return ERR_CAST(ubi);
} }
ubi_get_volume_info(ubi, &vi);
dbg_gen("opened ubi%d_%d", vi.ubi_num, vi.vol_id); c = alloc_ubifs_info(ubi);
if (!c) {
err = -ENOMEM;
goto out_close;
}
sb = sget(fs_type, &sb_test, &set_anon_super, &vi.cdev); dbg_gen("opened ubi%d_%d", c->vi.ubi_num, c->vi.vol_id);
sb = sget(fs_type, sb_test, sb_set, c);
if (IS_ERR(sb)) { if (IS_ERR(sb)) {
err = PTR_ERR(sb); err = PTR_ERR(sb);
goto out_close; kfree(c);
} }
if (sb->s_root) { if (sb->s_root) {
struct ubifs_info *c1 = sb->s_fs_info; struct ubifs_info *c1 = sb->s_fs_info;
kfree(c);
/* A new mount point for already mounted UBIFS */ /* A new mount point for already mounted UBIFS */
dbg_gen("this ubi volume is already mounted"); dbg_gen("this ubi volume is already mounted");
if (!!(flags & MS_RDONLY) != c1->ro_mount) { if (!!(flags & MS_RDONLY) != c1->ro_mount) {
...@@ -2146,11 +2159,6 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, ...@@ -2146,11 +2159,6 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
} }
} else { } else {
sb->s_flags = flags; sb->s_flags = flags;
/*
* Pass 'ubi' to 'fill_super()' in sb->s_fs_info where it is
* replaced by 'c'.
*/
sb->s_fs_info = ubi;
err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); err = ubifs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
if (err) if (err)
goto out_deact; goto out_deact;
...@@ -2170,11 +2178,18 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, ...@@ -2170,11 +2178,18 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags,
return ERR_PTR(err); return ERR_PTR(err);
} }
static void kill_ubifs_super(struct super_block *s)
{
struct ubifs_info *c = s->s_fs_info;
kill_anon_super(s);
kfree(c);
}
static struct file_system_type ubifs_fs_type = { static struct file_system_type ubifs_fs_type = {
.name = "ubifs", .name = "ubifs",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.mount = ubifs_mount, .mount = ubifs_mount,
.kill_sb = kill_anon_super, .kill_sb = kill_ubifs_super,
}; };
/* /*
......
...@@ -32,15 +32,17 @@ enum kobj_ns_type { ...@@ -32,15 +32,17 @@ enum kobj_ns_type {
/* /*
* Callbacks so sysfs can determine namespaces * Callbacks so sysfs can determine namespaces
* @current_ns: return calling task's namespace * @grab_current_ns: return a new reference to calling task's namespace
* @netlink_ns: return namespace to which a sock belongs (right?) * @netlink_ns: return namespace to which a sock belongs (right?)
* @initial_ns: return the initial namespace (i.e. init_net_ns) * @initial_ns: return the initial namespace (i.e. init_net_ns)
* @drop_ns: drops a reference to namespace
*/ */
struct kobj_ns_type_operations { struct kobj_ns_type_operations {
enum kobj_ns_type type; enum kobj_ns_type type;
const void *(*current_ns)(void); void *(*grab_current_ns)(void);
const void *(*netlink_ns)(struct sock *sk); const void *(*netlink_ns)(struct sock *sk);
const void *(*initial_ns)(void); const void *(*initial_ns)(void);
void (*drop_ns)(void *);
}; };
int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); int kobj_ns_type_register(const struct kobj_ns_type_operations *ops);
...@@ -48,9 +50,9 @@ int kobj_ns_type_registered(enum kobj_ns_type type); ...@@ -48,9 +50,9 @@ int kobj_ns_type_registered(enum kobj_ns_type type);
const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent);
const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj);
const void *kobj_ns_current(enum kobj_ns_type type); void *kobj_ns_grab_current(enum kobj_ns_type type);
const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk);
const void *kobj_ns_initial(enum kobj_ns_type type); const void *kobj_ns_initial(enum kobj_ns_type type);
void kobj_ns_exit(enum kobj_ns_type type, const void *ns); void kobj_ns_drop(enum kobj_ns_type type, void *ns);
#endif /* _LINUX_KOBJECT_NS_H */ #endif /* _LINUX_KOBJECT_NS_H */
...@@ -177,9 +177,6 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, ...@@ -177,9 +177,6 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd);
void sysfs_put(struct sysfs_dirent *sd); void sysfs_put(struct sysfs_dirent *sd);
/* Called to clear a ns tag when it is no longer valid */
void sysfs_exit_ns(enum kobj_ns_type type, const void *tag);
int __must_check sysfs_init(void); int __must_check sysfs_init(void);
#else /* CONFIG_SYSFS */ #else /* CONFIG_SYSFS */
...@@ -338,10 +335,6 @@ static inline void sysfs_put(struct sysfs_dirent *sd) ...@@ -338,10 +335,6 @@ static inline void sysfs_put(struct sysfs_dirent *sd)
{ {
} }
static inline void sysfs_exit_ns(int type, const void *tag)
{
}
static inline int __must_check sysfs_init(void) static inline int __must_check sysfs_init(void)
{ {
return 0; return 0;
......
...@@ -35,9 +35,12 @@ struct netns_ipvs; ...@@ -35,9 +35,12 @@ struct netns_ipvs;
#define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS) #define NETDEV_HASHENTRIES (1 << NETDEV_HASHBITS)
struct net { struct net {
atomic_t count; /* To decided when the network atomic_t passive; /* To decided when the network
* namespace should be freed. * namespace should be freed.
*/ */
atomic_t count; /* To decided when the network
* namespace should be shut down.
*/
#ifdef NETNS_REFCNT_DEBUG #ifdef NETNS_REFCNT_DEBUG
atomic_t use_count; /* To track references we atomic_t use_count; /* To track references we
* destroy on demand * destroy on demand
...@@ -154,6 +157,9 @@ int net_eq(const struct net *net1, const struct net *net2) ...@@ -154,6 +157,9 @@ int net_eq(const struct net *net1, const struct net *net2)
{ {
return net1 == net2; return net1 == net2;
} }
extern void net_drop_ns(void *);
#else #else
static inline struct net *get_net(struct net *net) static inline struct net *get_net(struct net *net)
...@@ -175,6 +181,8 @@ int net_eq(const struct net *net1, const struct net *net2) ...@@ -175,6 +181,8 @@ int net_eq(const struct net *net1, const struct net *net2)
{ {
return 1; return 1;
} }
#define net_drop_ns NULL
#endif #endif
......
...@@ -948,14 +948,14 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) ...@@ -948,14 +948,14 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj)
} }
const void *kobj_ns_current(enum kobj_ns_type type) void *kobj_ns_grab_current(enum kobj_ns_type type)
{ {
const void *ns = NULL; void *ns = NULL;
spin_lock(&kobj_ns_type_lock); spin_lock(&kobj_ns_type_lock);
if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
kobj_ns_ops_tbl[type]) kobj_ns_ops_tbl[type])
ns = kobj_ns_ops_tbl[type]->current_ns(); ns = kobj_ns_ops_tbl[type]->grab_current_ns();
spin_unlock(&kobj_ns_type_lock); spin_unlock(&kobj_ns_type_lock);
return ns; return ns;
...@@ -987,23 +987,15 @@ const void *kobj_ns_initial(enum kobj_ns_type type) ...@@ -987,23 +987,15 @@ const void *kobj_ns_initial(enum kobj_ns_type type)
return ns; return ns;
} }
/* void kobj_ns_drop(enum kobj_ns_type type, void *ns)
* kobj_ns_exit - invalidate a namespace tag
*
* @type: the namespace type (i.e. KOBJ_NS_TYPE_NET)
* @ns: the actual namespace being invalidated
*
* This is called when a tag is no longer valid. For instance,
* when a network namespace exits, it uses this helper to
* make sure no sb's sysfs_info points to the now-invalidated
* netns.
*/
void kobj_ns_exit(enum kobj_ns_type type, const void *ns)
{ {
sysfs_exit_ns(type, ns); spin_lock(&kobj_ns_type_lock);
if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) &&
kobj_ns_ops_tbl[type] && kobj_ns_ops_tbl[type]->drop_ns)
kobj_ns_ops_tbl[type]->drop_ns(ns);
spin_unlock(&kobj_ns_type_lock);
} }
EXPORT_SYMBOL(kobject_get); EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(kobject_del); EXPORT_SYMBOL(kobject_del);
......
...@@ -1179,9 +1179,14 @@ static void remove_queue_kobjects(struct net_device *net) ...@@ -1179,9 +1179,14 @@ static void remove_queue_kobjects(struct net_device *net)
#endif #endif
} }
static const void *net_current_ns(void) static void *net_grab_current_ns(void)
{ {
return current->nsproxy->net_ns; struct net *ns = current->nsproxy->net_ns;
#ifdef CONFIG_NET_NS
if (ns)
atomic_inc(&ns->passive);
#endif
return ns;
} }
static const void *net_initial_ns(void) static const void *net_initial_ns(void)
...@@ -1196,22 +1201,13 @@ static const void *net_netlink_ns(struct sock *sk) ...@@ -1196,22 +1201,13 @@ static const void *net_netlink_ns(struct sock *sk)
struct kobj_ns_type_operations net_ns_type_operations = { struct kobj_ns_type_operations net_ns_type_operations = {
.type = KOBJ_NS_TYPE_NET, .type = KOBJ_NS_TYPE_NET,
.current_ns = net_current_ns, .grab_current_ns = net_grab_current_ns,
.netlink_ns = net_netlink_ns, .netlink_ns = net_netlink_ns,
.initial_ns = net_initial_ns, .initial_ns = net_initial_ns,
.drop_ns = net_drop_ns,
}; };
EXPORT_SYMBOL_GPL(net_ns_type_operations); EXPORT_SYMBOL_GPL(net_ns_type_operations);
static void net_kobj_ns_exit(struct net *net)
{
kobj_ns_exit(KOBJ_NS_TYPE_NET, net);
}
static struct pernet_operations kobj_net_ops = {
.exit = net_kobj_ns_exit,
};
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
{ {
...@@ -1339,6 +1335,5 @@ EXPORT_SYMBOL(netdev_class_remove_file); ...@@ -1339,6 +1335,5 @@ EXPORT_SYMBOL(netdev_class_remove_file);
int netdev_kobject_init(void) int netdev_kobject_init(void)
{ {
kobj_ns_type_register(&net_ns_type_operations); kobj_ns_type_register(&net_ns_type_operations);
register_pernet_subsys(&kobj_net_ops);
return class_register(&net_class); return class_register(&net_class);
} }
...@@ -128,6 +128,7 @@ static __net_init int setup_net(struct net *net) ...@@ -128,6 +128,7 @@ static __net_init int setup_net(struct net *net)
LIST_HEAD(net_exit_list); LIST_HEAD(net_exit_list);
atomic_set(&net->count, 1); atomic_set(&net->count, 1);
atomic_set(&net->passive, 1);
#ifdef NETNS_REFCNT_DEBUG #ifdef NETNS_REFCNT_DEBUG
atomic_set(&net->use_count, 0); atomic_set(&net->use_count, 0);
...@@ -210,6 +211,13 @@ static void net_free(struct net *net) ...@@ -210,6 +211,13 @@ static void net_free(struct net *net)
kmem_cache_free(net_cachep, net); kmem_cache_free(net_cachep, net);
} }
void net_drop_ns(void *p)
{
struct net *ns = p;
if (ns && atomic_dec_and_test(&ns->passive))
net_free(ns);
}
struct net *copy_net_ns(unsigned long flags, struct net *old_net) struct net *copy_net_ns(unsigned long flags, struct net *old_net)
{ {
struct net *net; struct net *net;
...@@ -230,7 +238,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net) ...@@ -230,7 +238,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
} }
mutex_unlock(&net_mutex); mutex_unlock(&net_mutex);
if (rv < 0) { if (rv < 0) {
net_free(net); net_drop_ns(net);
return ERR_PTR(rv); return ERR_PTR(rv);
} }
return net; return net;
...@@ -286,7 +294,7 @@ static void cleanup_net(struct work_struct *work) ...@@ -286,7 +294,7 @@ static void cleanup_net(struct work_struct *work)
/* Finally it is safe to free my network namespace structure */ /* Finally it is safe to free my network namespace structure */
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) { list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
list_del_init(&net->exit_list); list_del_init(&net->exit_list);
net_free(net); net_drop_ns(net);
} }
} }
static DECLARE_WORK(net_cleanup_work, cleanup_net); static DECLARE_WORK(net_cleanup_work, cleanup_net);
......
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