*
* nfs superblock handling functions
*
- * Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
+ * Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some
* experimental NFS changes. Modularisation taken straight from SYS5 fs.
*
* Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
#include "delegation.h"
#include "iostat.h"
#include "internal.h"
+#include "fscache.h"
#define NFSDBG_FACILITY NFSDBG_VFS
Opt_acl, Opt_noacl,
Opt_rdirplus, Opt_nordirplus,
Opt_sharecache, Opt_nosharecache,
+ Opt_resvport, Opt_noresvport,
+ Opt_fscache, Opt_nofscache,
/* Mount options that take integer arguments */
Opt_port,
/* Mount options that take string arguments */
Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
Opt_addr, Opt_mountaddr, Opt_clientaddr,
+ Opt_lookupcache,
+ Opt_fscache_uniq,
/* Special mount options */
Opt_userspace, Opt_deprecated, Opt_sloppy,
Opt_err
};
-static match_table_t nfs_mount_option_tokens = {
+static const match_table_t nfs_mount_option_tokens = {
{ Opt_userspace, "bg" },
{ Opt_userspace, "fg" },
{ Opt_userspace, "retry=%s" },
{ Opt_nordirplus, "nordirplus" },
{ Opt_sharecache, "sharecache" },
{ Opt_nosharecache, "nosharecache" },
+ { Opt_resvport, "resvport" },
+ { Opt_noresvport, "noresvport" },
+ { Opt_fscache, "fsc" },
+ { Opt_fscache_uniq, "fsc=%s" },
+ { Opt_nofscache, "nofsc" },
{ Opt_port, "port=%u" },
{ Opt_rsize, "rsize=%u" },
{ Opt_mounthost, "mounthost=%s" },
{ Opt_mountaddr, "mountaddr=%s" },
+ { Opt_lookupcache, "lookupcache=%s" },
+
{ Opt_err, NULL }
};
Opt_xprt_err
};
-static match_table_t nfs_xprt_protocol_tokens = {
+static const match_table_t nfs_xprt_protocol_tokens = {
{ Opt_xprt_udp, "udp" },
{ Opt_xprt_tcp, "tcp" },
{ Opt_xprt_rdma, "rdma" },
Opt_sec_err
};
-static match_table_t nfs_secflavor_tokens = {
+static const match_table_t nfs_secflavor_tokens = {
{ Opt_sec_none, "none" },
{ Opt_sec_none, "null" },
{ Opt_sec_sys, "sys" },
{ Opt_sec_err, NULL }
};
+enum {
+ Opt_lookupcache_all, Opt_lookupcache_positive,
+ Opt_lookupcache_none,
+
+ Opt_lookupcache_err
+};
+
+static match_table_t nfs_lookupcache_tokens = {
+ { Opt_lookupcache_all, "all" },
+ { Opt_lookupcache_positive, "pos" },
+ { Opt_lookupcache_positive, "positive" },
+ { Opt_lookupcache_none, "none" },
+
+ { Opt_lookupcache_err, NULL }
+};
+
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *);
static int nfs_xdev_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
static void nfs_kill_super(struct super_block *);
-static void nfs_put_super(struct super_block *);
static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
static struct file_system_type nfs_fs_type = {
.alloc_inode = nfs_alloc_inode,
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
- .put_super = nfs_put_super,
.statfs = nfs_statfs,
.clear_inode = nfs_clear_inode,
.umount_begin = nfs_umount_begin,
unregister_filesystem(&nfs_fs_type);
}
-void nfs_sb_active(struct nfs_server *server)
+void nfs_sb_active(struct super_block *sb)
{
- atomic_inc(&server->active);
-}
+ struct nfs_server *server = NFS_SB(sb);
-void nfs_sb_deactive(struct nfs_server *server)
-{
- if (atomic_dec_and_test(&server->active))
- wake_up(&server->active_wq);
+ if (atomic_inc_return(&server->active) == 1)
+ atomic_inc(&sb->s_active);
}
-static void nfs_put_super(struct super_block *sb)
+void nfs_sb_deactive(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
- /*
- * Make sure there are no outstanding ops to this server.
- * If so, wait for them to finish before allowing the
- * unmount to continue.
- */
- wait_event(server->active_wq, atomic_read(&server->active) == 0);
+
+ if (atomic_dec_and_test(&server->active))
+ deactivate_super(sb);
}
/*
};
int error;
- lock_kernel();
-
error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
if (error < 0)
goto out_err;
buf->f_namelen = server->namelen;
- unlock_kernel();
return 0;
out_err:
dprintk("%s: statfs error = %d\n", __func__, -error);
- unlock_kernel();
return error;
}
switch (sap->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
- seq_printf(m, ",mountaddr=" NIPQUAD_FMT,
- NIPQUAD(sin->sin_addr.s_addr));
+ seq_printf(m, ",mountaddr=%pI4", &sin->sin_addr.s_addr);
break;
}
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
- seq_printf(m, ",mountaddr=" NIP6_FMT,
- NIP6(sin6->sin6_addr));
+ seq_printf(m, ",mountaddr=%pI6", &sin6->sin6_addr);
break;
}
default:
{ NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
- { NFS_MOUNT_UNSHARED, ",nosharecache", ""},
+ { NFS_MOUNT_UNSHARED, ",nosharecache", "" },
+ { NFS_MOUNT_NORESVPORT, ",noresvport", "" },
{ 0, NULL, NULL }
};
const struct proc_nfs_info *nfs_infop;
if (clp->rpc_ops->version == 4)
seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr);
#endif
+ if (nfss->options & NFS_OPTION_FSCACHE)
+ seq_printf(m, ",fsc");
}
/*
totals.events[i] += stats->events[i];
for (i = 0; i < __NFSIOS_BYTESMAX; i++)
totals.bytes[i] += stats->bytes[i];
+#ifdef CONFIG_NFS_FSCACHE
+ for (i = 0; i < __NFSIOS_FSCACHEMAX; i++)
+ totals.fscache[i] += stats->fscache[i];
+#endif
preempt_enable();
}
seq_printf(m, "\n\tbytes:\t");
for (i = 0; i < __NFSIOS_BYTESMAX; i++)
seq_printf(m, "%Lu ", totals.bytes[i]);
+#ifdef CONFIG_NFS_FSCACHE
+ if (nfss->options & NFS_OPTION_FSCACHE) {
+ seq_printf(m, "\n\tfsc:\t");
+ for (i = 0; i < __NFSIOS_FSCACHEMAX; i++)
+ seq_printf(m, "%Lu ", totals.bytes[i]);
+ }
+#endif
seq_printf(m, "\n");
rpc_print_iostats(m, nfss->client);
*/
static void nfs_umount_begin(struct super_block *sb)
{
- struct nfs_server *server = NFS_SB(sb);
+ struct nfs_server *server;
struct rpc_clnt *rpc;
+ lock_kernel();
+
+ server = NFS_SB(sb);
/* -EIO all pending I/O */
rpc = server->client_acl;
if (!IS_ERR(rpc))
rpc = server->client;
if (!IS_ERR(rpc))
rpc_killall_tasks(rpc);
-}
-/*
- * Set the port number in an address. Be agnostic about the address family.
- */
-static void nfs_set_port(struct sockaddr *sap, unsigned short port)
-{
- switch (sap->sa_family) {
- case AF_INET: {
- struct sockaddr_in *ap = (struct sockaddr_in *)sap;
- ap->sin_port = htons(port);
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
- ap->sin6_port = htons(port);
- break;
- }
- }
+ unlock_kernel();
}
/*
*addr_len = 0;
}
-#define IPV6_SCOPE_DELIMITER '%'
-
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
- const char *delim,
- struct sockaddr_in6 *sin6)
+static int nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
+ const char *delim,
+ struct sockaddr_in6 *sin6)
{
char *p;
size_t len;
- if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
- return ;
+ if ((string + str_len) == delim)
+ return 1;
+
if (*delim != IPV6_SCOPE_DELIMITER)
- return;
+ return 0;
+
+ if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
+ return 0;
len = (string + str_len) - delim - 1;
p = kstrndup(delim + 1, len, GFP_KERNEL);
scope_id = dev->ifindex;
dev_put(dev);
} else {
- /* scope_id is set to zero on error */
- strict_strtoul(p, 10, &scope_id);
+ if (strict_strtoul(p, 10, &scope_id) == 0) {
+ kfree(p);
+ return 0;
+ }
}
kfree(p);
+
sin6->sin6_scope_id = scope_id;
dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
+ return 1;
}
+
+ return 0;
}
static void nfs_parse_ipv6_address(char *string, size_t str_len,
sin6->sin6_family = AF_INET6;
*addr_len = sizeof(*sin6);
- if (in6_pton(string, str_len, addr, IPV6_SCOPE_DELIMITER, &delim)) {
- nfs_parse_ipv6_scope_id(string, str_len, delim, sin6);
- return;
+ if (in6_pton(string, str_len, addr,
+ IPV6_SCOPE_DELIMITER, &delim) != 0) {
+ if (nfs_parse_ipv6_scope_id(string, str_len,
+ delim, sin6) != 0)
+ return;
}
}
* If there is a problem constructing the new sockaddr, set the address
* family to AF_UNSPEC.
*/
-static void nfs_parse_ip_address(char *string, size_t str_len,
+void nfs_parse_ip_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
unsigned int i, colons;
case Opt_rdma:
mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
+ xprt_load_transport(p);
break;
case Opt_acl:
mnt->flags &= ~NFS_MOUNT_NOACL;
case Opt_nosharecache:
mnt->flags |= NFS_MOUNT_UNSHARED;
break;
+ case Opt_resvport:
+ mnt->flags &= ~NFS_MOUNT_NORESVPORT;
+ break;
+ case Opt_noresvport:
+ mnt->flags |= NFS_MOUNT_NORESVPORT;
+ break;
+ case Opt_fscache:
+ mnt->options |= NFS_OPTION_FSCACHE;
+ kfree(mnt->fscache_uniq);
+ mnt->fscache_uniq = NULL;
+ break;
+ case Opt_nofscache:
+ mnt->options &= ~NFS_OPTION_FSCACHE;
+ kfree(mnt->fscache_uniq);
+ mnt->fscache_uniq = NULL;
+ break;
+ case Opt_fscache_uniq:
+ string = match_strdup(args);
+ if (!string)
+ goto out_nomem;
+ kfree(mnt->fscache_uniq);
+ mnt->fscache_uniq = string;
+ mnt->options |= NFS_OPTION_FSCACHE;
+ break;
/*
* options that take numeric values
goto out_nomem;
token = match_token(string,
nfs_xprt_protocol_tokens, args);
- kfree(string);
switch (token) {
case Opt_xprt_udp:
/* vector side protocols to TCP */
mnt->flags |= NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
+ xprt_load_transport(string);
break;
default:
errors++;
dfprintk(MOUNT, "NFS: unrecognized "
"transport protocol\n");
}
+ kfree(string);
break;
case Opt_mountproto:
string = match_strdup(args);
&mnt->mount_server.addrlen);
kfree(string);
break;
+ case Opt_lookupcache:
+ string = match_strdup(args);
+ if (string == NULL)
+ goto out_nomem;
+ token = match_token(string,
+ nfs_lookupcache_tokens, args);
+ kfree(string);
+ switch (token) {
+ case Opt_lookupcache_all:
+ mnt->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
+ break;
+ case Opt_lookupcache_positive:
+ mnt->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
+ mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
+ break;
+ case Opt_lookupcache_none:
+ mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
+ break;
+ default:
+ errors++;
+ dfprintk(MOUNT, "NFS: invalid "
+ "lookupcache argument\n");
+ };
+ break;
/*
* Special options
}
}
+ if (errors > 0) {
+ dfprintk(MOUNT, "NFS: parsing encountered %d error%s\n",
+ errors, (errors == 1 ? "" : "s"));
+ if (!sloppy)
+ return 0;
+ }
return 1;
out_nomem:
static int nfs_try_mount(struct nfs_parsed_mount_data *args,
struct nfs_fh *root_fh)
{
- struct sockaddr *sap = (struct sockaddr *)&args->mount_server.address;
- char *hostname;
+ struct nfs_mount_request request = {
+ .sap = (struct sockaddr *)
+ &args->mount_server.address,
+ .dirpath = args->nfs_server.export_path,
+ .protocol = args->mount_server.protocol,
+ .fh = root_fh,
+ .noresvport = args->flags & NFS_MOUNT_NORESVPORT,
+ };
int status;
if (args->mount_server.version == 0) {
else
args->mount_server.version = NFS_MNT_VERSION;
}
+ request.version = args->mount_server.version;
if (args->mount_server.hostname)
- hostname = args->mount_server.hostname;
+ request.hostname = args->mount_server.hostname;
else
- hostname = args->nfs_server.hostname;
+ request.hostname = args->nfs_server.hostname;
/*
* Construct the mount server's address.
*/
if (args->mount_server.address.ss_family == AF_UNSPEC) {
- memcpy(sap, &args->nfs_server.address,
+ memcpy(request.sap, &args->nfs_server.address,
args->nfs_server.addrlen);
args->mount_server.addrlen = args->nfs_server.addrlen;
}
+ request.salen = args->mount_server.addrlen;
/*
* autobind will be used if mount_server.port == 0
*/
- nfs_set_port(sap, args->mount_server.port);
+ nfs_set_port(request.sap, args->mount_server.port);
/*
* Now ask the mount server to map our export path
* to a file handle.
*/
- status = nfs_mount(sap,
- args->mount_server.addrlen,
- hostname,
- args->nfs_server.export_path,
- args->mount_server.version,
- args->mount_server.protocol,
- root_fh);
+ status = nfs_mount(&request);
if (status == 0)
return 0;
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
- hostname, status);
+ request.hostname, status);
return status;
}
* Translate to nfs_parsed_mount_data, which nfs_fill_super
* can deal with.
*/
- args->flags = data->flags;
+ args->flags = data->flags & NFS_MOUNT_FLAGMASK;
args->rsize = data->rsize;
args->wsize = data->wsize;
args->timeo = data->timeo;
* ones were explicitly specified. Fall back to legacy behavior and
* just return success.
*/
- if ((nfsvers == 4 && options4->version == 1) ||
- (nfsvers <= 3 && options->version >= 1 &&
- options->version <= 6))
+ if ((nfsvers == 4 && (!options4 || options4->version == 1)) ||
+ (nfsvers <= 3 && (!options || (options->version >= 1 &&
+ options->version <= 6))))
return 0;
data = kzalloc(sizeof(*data), GFP_KERNEL);
nfs_initialise_sb(sb);
}
-#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
-
static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags)
{
const struct nfs_server *a = s->s_fs_info;
if (!s->s_root) {
/* initial superblock/root creation */
nfs_fill_super(s, data);
+ nfs_fscache_get_super_cookie(s, data);
}
mntroot = nfs_get_root(s, mntfh);
out:
kfree(data->nfs_server.hostname);
kfree(data->mount_server.hostname);
+ kfree(data->fscache_uniq);
security_free_mnt_opts(&data->lsm_opts);
out_free_fh:
kfree(mntfh);
error_splat_root:
dput(mntroot);
error_splat_super:
- up_write(&s->s_umount);
- deactivate_super(s);
+ deactivate_locked_super(s);
goto out;
}
bdi_unregister(&server->backing_dev_info);
kill_anon_super(s);
+ nfs_fscache_release_super_cookie(s);
nfs_free_server(server);
}
return error;
error_splat_super:
- up_write(&s->s_umount);
- deactivate_super(s);
+ deactivate_locked_super(s);
dprintk("<-- nfs_xdev_get_sb() = %d [splat]\n", error);
return error;
}
if (!s->s_root) {
/* initial superblock/root creation */
nfs4_fill_super(s);
+ nfs_fscache_get_super_cookie(s, data);
}
mntroot = nfs4_get_root(s, mntfh);
kfree(data->client_address);
kfree(data->nfs_server.export_path);
kfree(data->nfs_server.hostname);
+ kfree(data->fscache_uniq);
security_free_mnt_opts(&data->lsm_opts);
out_free_fh:
kfree(mntfh);
error_splat_root:
dput(mntroot);
error_splat_super:
- up_write(&s->s_umount);
- deactivate_super(s);
+ deactivate_locked_super(s);
goto out;
}
{
struct nfs_server *server = NFS_SB(sb);
- nfs_return_all_delegations(sb);
+ nfs_super_return_all_delegations(sb);
kill_anon_super(sb);
nfs4_renewd_prepare_shutdown(server);
+ nfs_fscache_release_super_cookie(sb);
nfs_free_server(server);
}
compare_super = NULL;
/* Get a superblock - note that we may end up sharing one that already exists */
- s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
+ s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
if (IS_ERR(s)) {
error = PTR_ERR(s);
goto out_err_nosb;
return error;
error_splat_super:
- up_write(&s->s_umount);
- deactivate_super(s);
+ deactivate_locked_super(s);
dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error);
return error;
}
compare_super = NULL;
/* Get a superblock - note that we may end up sharing one that already exists */
- s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata);
+ s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata);
if (IS_ERR(s)) {
error = PTR_ERR(s);
goto out_err_nosb;
return error;
error_splat_super:
- up_write(&s->s_umount);
- deactivate_super(s);
+ deactivate_locked_super(s);
dprintk("<-- nfs4_referral_get_sb() = %d [splat]\n", error);
return error;
}