diff options
| author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-08-31 10:45:17 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-08-31 23:26:45 -0400 |
| commit | e89a5a43b95cdc4305b7c8e8121a380f02476636 (patch) | |
| tree | ba286205f1ad11119a06b77fb6671ce9aacac13a | |
| parent | 36ad4885c47c2187822f2783fb46fde2d36bf200 (diff) | |
NFS: Fix the mount regression
This avoids the recent NFS mount regression (returning EBUSY when
mounting the same filesystem twice with different parameters).
The best I can do given the constraints appears to be to have the kernel
first look for a superblock that matches both the fsid and the
user-specified mount options, and then spawn off a new superblock if
that search fails.
Note that this is not the same as specifying nosharecache everywhere
since nosharecache will never attempt to match an existing superblock.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Tested-by: Hua Zhong <hzhong@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/nfs/super.c | 110 |
1 files changed, 64 insertions, 46 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b2a851c1b8cb..46139003ea0c 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
| @@ -1303,34 +1303,6 @@ static void nfs_clone_super(struct super_block *sb, | |||
| 1303 | nfs_initialise_sb(sb); | 1303 | nfs_initialise_sb(sb); |
| 1304 | } | 1304 | } |
| 1305 | 1305 | ||
| 1306 | static int nfs_set_super(struct super_block *s, void *_server) | ||
| 1307 | { | ||
| 1308 | struct nfs_server *server = _server; | ||
| 1309 | int ret; | ||
| 1310 | |||
| 1311 | s->s_fs_info = server; | ||
| 1312 | ret = set_anon_super(s, server); | ||
| 1313 | if (ret == 0) | ||
| 1314 | server->s_dev = s->s_dev; | ||
| 1315 | return ret; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | static int nfs_compare_super(struct super_block *sb, void *data) | ||
| 1319 | { | ||
| 1320 | struct nfs_server *server = data, *old = NFS_SB(sb); | ||
| 1321 | |||
| 1322 | if (memcmp(&old->nfs_client->cl_addr, | ||
| 1323 | &server->nfs_client->cl_addr, | ||
| 1324 | sizeof(old->nfs_client->cl_addr)) != 0) | ||
| 1325 | return 0; | ||
| 1326 | /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ | ||
| 1327 | if (old->flags & NFS_MOUNT_UNSHARED) | ||
| 1328 | return 0; | ||
| 1329 | if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) | ||
| 1330 | return 0; | ||
| 1331 | return 1; | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | #define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) | 1306 | #define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS) |
| 1335 | 1307 | ||
| 1336 | static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) | 1308 | static int nfs_compare_mount_options(const struct super_block *s, const struct nfs_server *b, int flags) |
| @@ -1359,9 +1331,46 @@ static int nfs_compare_mount_options(const struct super_block *s, const struct n | |||
| 1359 | goto Ebusy; | 1331 | goto Ebusy; |
| 1360 | if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) | 1332 | if (clnt_a->cl_auth->au_flavor != clnt_b->cl_auth->au_flavor) |
| 1361 | goto Ebusy; | 1333 | goto Ebusy; |
| 1362 | return 0; | 1334 | return 1; |
| 1363 | Ebusy: | 1335 | Ebusy: |
| 1364 | return -EBUSY; | 1336 | return 0; |
| 1337 | } | ||
| 1338 | |||
| 1339 | struct nfs_sb_mountdata { | ||
| 1340 | struct nfs_server *server; | ||
| 1341 | int mntflags; | ||
| 1342 | }; | ||
| 1343 | |||
| 1344 | static int nfs_set_super(struct super_block *s, void *data) | ||
| 1345 | { | ||
| 1346 | struct nfs_sb_mountdata *sb_mntdata = data; | ||
| 1347 | struct nfs_server *server = sb_mntdata->server; | ||
| 1348 | int ret; | ||
| 1349 | |||
| 1350 | s->s_flags = sb_mntdata->mntflags; | ||
| 1351 | s->s_fs_info = server; | ||
| 1352 | ret = set_anon_super(s, server); | ||
| 1353 | if (ret == 0) | ||
| 1354 | server->s_dev = s->s_dev; | ||
| 1355 | return ret; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | static int nfs_compare_super(struct super_block *sb, void *data) | ||
| 1359 | { | ||
| 1360 | struct nfs_sb_mountdata *sb_mntdata = data; | ||
| 1361 | struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb); | ||
| 1362 | int mntflags = sb_mntdata->mntflags; | ||
| 1363 | |||
| 1364 | if (memcmp(&old->nfs_client->cl_addr, | ||
| 1365 | &server->nfs_client->cl_addr, | ||
| 1366 | sizeof(old->nfs_client->cl_addr)) != 0) | ||
| 1367 | return 0; | ||
| 1368 | /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */ | ||
| 1369 | if (old->flags & NFS_MOUNT_UNSHARED) | ||
| 1370 | return 0; | ||
| 1371 | if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) | ||
| 1372 | return 0; | ||
| 1373 | return nfs_compare_mount_options(sb, server, mntflags); | ||
| 1365 | } | 1374 | } |
| 1366 | 1375 | ||
| 1367 | static int nfs_get_sb(struct file_system_type *fs_type, | 1376 | static int nfs_get_sb(struct file_system_type *fs_type, |
| @@ -1373,6 +1382,9 @@ static int nfs_get_sb(struct file_system_type *fs_type, | |||
| 1373 | struct nfs_mount_data *data = raw_data; | 1382 | struct nfs_mount_data *data = raw_data; |
| 1374 | struct dentry *mntroot; | 1383 | struct dentry *mntroot; |
| 1375 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | 1384 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
| 1385 | struct nfs_sb_mountdata sb_mntdata = { | ||
| 1386 | .mntflags = flags, | ||
| 1387 | }; | ||
| 1376 | int error; | 1388 | int error; |
| 1377 | 1389 | ||
| 1378 | /* Validate the mount data */ | 1390 | /* Validate the mount data */ |
| @@ -1386,28 +1398,25 @@ static int nfs_get_sb(struct file_system_type *fs_type, | |||
| 1386 | error = PTR_ERR(server); | 1398 | error = PTR_ERR(server); |
| 1387 | goto out; | 1399 | goto out; |
| 1388 | } | 1400 | } |
| 1401 | sb_mntdata.server = server; | ||
| 1389 | 1402 | ||
| 1390 | if (server->flags & NFS_MOUNT_UNSHARED) | 1403 | if (server->flags & NFS_MOUNT_UNSHARED) |
| 1391 | compare_super = NULL; | 1404 | compare_super = NULL; |
| 1392 | 1405 | ||
| 1393 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1406 | /* Get a superblock - note that we may end up sharing one that already exists */ |
| 1394 | s = sget(fs_type, compare_super, nfs_set_super, server); | 1407 | s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); |
| 1395 | if (IS_ERR(s)) { | 1408 | if (IS_ERR(s)) { |
| 1396 | error = PTR_ERR(s); | 1409 | error = PTR_ERR(s); |
| 1397 | goto out_err_nosb; | 1410 | goto out_err_nosb; |
| 1398 | } | 1411 | } |
| 1399 | 1412 | ||
| 1400 | if (s->s_fs_info != server) { | 1413 | if (s->s_fs_info != server) { |
| 1401 | error = nfs_compare_mount_options(s, server, flags); | ||
| 1402 | nfs_free_server(server); | 1414 | nfs_free_server(server); |
| 1403 | server = NULL; | 1415 | server = NULL; |
| 1404 | if (error < 0) | ||
| 1405 | goto error_splat_super; | ||
| 1406 | } | 1416 | } |
| 1407 | 1417 | ||
| 1408 | if (!s->s_root) { | 1418 | if (!s->s_root) { |
| 1409 | /* initial superblock/root creation */ | 1419 | /* initial superblock/root creation */ |
| 1410 | s->s_flags = flags; | ||
| 1411 | nfs_fill_super(s, data); | 1420 | nfs_fill_super(s, data); |
| 1412 | } | 1421 | } |
| 1413 | 1422 | ||
| @@ -1460,6 +1469,9 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1460 | struct nfs_server *server; | 1469 | struct nfs_server *server; |
| 1461 | struct dentry *mntroot; | 1470 | struct dentry *mntroot; |
| 1462 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | 1471 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
| 1472 | struct nfs_sb_mountdata sb_mntdata = { | ||
| 1473 | .mntflags = flags, | ||
| 1474 | }; | ||
| 1463 | int error; | 1475 | int error; |
| 1464 | 1476 | ||
| 1465 | dprintk("--> nfs_xdev_get_sb()\n"); | 1477 | dprintk("--> nfs_xdev_get_sb()\n"); |
| @@ -1470,28 +1482,25 @@ static int nfs_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1470 | error = PTR_ERR(server); | 1482 | error = PTR_ERR(server); |
| 1471 | goto out_err_noserver; | 1483 | goto out_err_noserver; |
| 1472 | } | 1484 | } |
| 1485 | sb_mntdata.server = server; | ||
| 1473 | 1486 | ||
| 1474 | if (server->flags & NFS_MOUNT_UNSHARED) | 1487 | if (server->flags & NFS_MOUNT_UNSHARED) |
| 1475 | compare_super = NULL; | 1488 | compare_super = NULL; |
| 1476 | 1489 | ||
| 1477 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1490 | /* Get a superblock - note that we may end up sharing one that already exists */ |
| 1478 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); | 1491 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); |
| 1479 | if (IS_ERR(s)) { | 1492 | if (IS_ERR(s)) { |
| 1480 | error = PTR_ERR(s); | 1493 | error = PTR_ERR(s); |
| 1481 | goto out_err_nosb; | 1494 | goto out_err_nosb; |
| 1482 | } | 1495 | } |
| 1483 | 1496 | ||
| 1484 | if (s->s_fs_info != server) { | 1497 | if (s->s_fs_info != server) { |
| 1485 | error = nfs_compare_mount_options(s, server, flags); | ||
| 1486 | nfs_free_server(server); | 1498 | nfs_free_server(server); |
| 1487 | server = NULL; | 1499 | server = NULL; |
| 1488 | if (error < 0) | ||
| 1489 | goto error_splat_super; | ||
| 1490 | } | 1500 | } |
| 1491 | 1501 | ||
| 1492 | if (!s->s_root) { | 1502 | if (!s->s_root) { |
| 1493 | /* initial superblock/root creation */ | 1503 | /* initial superblock/root creation */ |
| 1494 | s->s_flags = flags; | ||
| 1495 | nfs_clone_super(s, data->sb); | 1504 | nfs_clone_super(s, data->sb); |
| 1496 | } | 1505 | } |
| 1497 | 1506 | ||
| @@ -1729,6 +1738,9 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
| 1729 | struct dentry *mntroot; | 1738 | struct dentry *mntroot; |
| 1730 | char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL; | 1739 | char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL; |
| 1731 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | 1740 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
| 1741 | struct nfs_sb_mountdata sb_mntdata = { | ||
| 1742 | .mntflags = flags, | ||
| 1743 | }; | ||
| 1732 | int error; | 1744 | int error; |
| 1733 | 1745 | ||
| 1734 | /* Validate the mount data */ | 1746 | /* Validate the mount data */ |
| @@ -1744,12 +1756,13 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
| 1744 | error = PTR_ERR(server); | 1756 | error = PTR_ERR(server); |
| 1745 | goto out; | 1757 | goto out; |
| 1746 | } | 1758 | } |
| 1759 | sb_mntdata.server = server; | ||
| 1747 | 1760 | ||
| 1748 | if (server->flags & NFS4_MOUNT_UNSHARED) | 1761 | if (server->flags & NFS4_MOUNT_UNSHARED) |
| 1749 | compare_super = NULL; | 1762 | compare_super = NULL; |
| 1750 | 1763 | ||
| 1751 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1764 | /* Get a superblock - note that we may end up sharing one that already exists */ |
| 1752 | s = sget(fs_type, compare_super, nfs_set_super, server); | 1765 | s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); |
| 1753 | if (IS_ERR(s)) { | 1766 | if (IS_ERR(s)) { |
| 1754 | error = PTR_ERR(s); | 1767 | error = PTR_ERR(s); |
| 1755 | goto out_free; | 1768 | goto out_free; |
| @@ -1762,7 +1775,6 @@ static int nfs4_get_sb(struct file_system_type *fs_type, | |||
| 1762 | 1775 | ||
| 1763 | if (!s->s_root) { | 1776 | if (!s->s_root) { |
| 1764 | /* initial superblock/root creation */ | 1777 | /* initial superblock/root creation */ |
| 1765 | s->s_flags = flags; | ||
| 1766 | nfs4_fill_super(s); | 1778 | nfs4_fill_super(s); |
| 1767 | } | 1779 | } |
| 1768 | 1780 | ||
| @@ -1816,6 +1828,9 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1816 | struct nfs_server *server; | 1828 | struct nfs_server *server; |
| 1817 | struct dentry *mntroot; | 1829 | struct dentry *mntroot; |
| 1818 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | 1830 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
| 1831 | struct nfs_sb_mountdata sb_mntdata = { | ||
| 1832 | .mntflags = flags, | ||
| 1833 | }; | ||
| 1819 | int error; | 1834 | int error; |
| 1820 | 1835 | ||
| 1821 | dprintk("--> nfs4_xdev_get_sb()\n"); | 1836 | dprintk("--> nfs4_xdev_get_sb()\n"); |
| @@ -1826,12 +1841,13 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1826 | error = PTR_ERR(server); | 1841 | error = PTR_ERR(server); |
| 1827 | goto out_err_noserver; | 1842 | goto out_err_noserver; |
| 1828 | } | 1843 | } |
| 1844 | sb_mntdata.server = server; | ||
| 1829 | 1845 | ||
| 1830 | if (server->flags & NFS4_MOUNT_UNSHARED) | 1846 | if (server->flags & NFS4_MOUNT_UNSHARED) |
| 1831 | compare_super = NULL; | 1847 | compare_super = NULL; |
| 1832 | 1848 | ||
| 1833 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1849 | /* Get a superblock - note that we may end up sharing one that already exists */ |
| 1834 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); | 1850 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); |
| 1835 | if (IS_ERR(s)) { | 1851 | if (IS_ERR(s)) { |
| 1836 | error = PTR_ERR(s); | 1852 | error = PTR_ERR(s); |
| 1837 | goto out_err_nosb; | 1853 | goto out_err_nosb; |
| @@ -1844,7 +1860,6 @@ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1844 | 1860 | ||
| 1845 | if (!s->s_root) { | 1861 | if (!s->s_root) { |
| 1846 | /* initial superblock/root creation */ | 1862 | /* initial superblock/root creation */ |
| 1847 | s->s_flags = flags; | ||
| 1848 | nfs4_clone_super(s, data->sb); | 1863 | nfs4_clone_super(s, data->sb); |
| 1849 | } | 1864 | } |
| 1850 | 1865 | ||
| @@ -1887,6 +1902,9 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1887 | struct dentry *mntroot; | 1902 | struct dentry *mntroot; |
| 1888 | struct nfs_fh mntfh; | 1903 | struct nfs_fh mntfh; |
| 1889 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; | 1904 | int (*compare_super)(struct super_block *, void *) = nfs_compare_super; |
| 1905 | struct nfs_sb_mountdata sb_mntdata = { | ||
| 1906 | .mntflags = flags, | ||
| 1907 | }; | ||
| 1890 | int error; | 1908 | int error; |
| 1891 | 1909 | ||
| 1892 | dprintk("--> nfs4_referral_get_sb()\n"); | 1910 | dprintk("--> nfs4_referral_get_sb()\n"); |
| @@ -1897,12 +1915,13 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1897 | error = PTR_ERR(server); | 1915 | error = PTR_ERR(server); |
| 1898 | goto out_err_noserver; | 1916 | goto out_err_noserver; |
| 1899 | } | 1917 | } |
| 1918 | sb_mntdata.server = server; | ||
| 1900 | 1919 | ||
| 1901 | if (server->flags & NFS4_MOUNT_UNSHARED) | 1920 | if (server->flags & NFS4_MOUNT_UNSHARED) |
| 1902 | compare_super = NULL; | 1921 | compare_super = NULL; |
| 1903 | 1922 | ||
| 1904 | /* Get a superblock - note that we may end up sharing one that already exists */ | 1923 | /* Get a superblock - note that we may end up sharing one that already exists */ |
| 1905 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, server); | 1924 | s = sget(&nfs_fs_type, compare_super, nfs_set_super, &sb_mntdata); |
| 1906 | if (IS_ERR(s)) { | 1925 | if (IS_ERR(s)) { |
| 1907 | error = PTR_ERR(s); | 1926 | error = PTR_ERR(s); |
| 1908 | goto out_err_nosb; | 1927 | goto out_err_nosb; |
| @@ -1915,7 +1934,6 @@ static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |||
| 1915 | 1934 | ||
| 1916 | if (!s->s_root) { | 1935 | if (!s->s_root) { |
| 1917 | /* initial superblock/root creation */ | 1936 | /* initial superblock/root creation */ |
| 1918 | s->s_flags = flags; | ||
| 1919 | nfs4_fill_super(s); | 1937 | nfs4_fill_super(s); |
| 1920 | } | 1938 | } |
| 1921 | 1939 | ||
