diff mbox series

[1/1] NFS: New mount option force_rdirplus

Message ID 4a471ab1bdea1052f45d894c967d0a6b6e38d4a6.1741806879.git.bcodding@redhat.com (mailing list archive)
State New
Headers show
Series Mount option - force READDIRPLUS | expand

Commit Message

Benjamin Coddington March 12, 2025, 7:46 p.m. UTC
There are certain users that wish to force the NFS client to choose
READDIRPLUS over READDIR for a particular mount.  Add a new kernel mount
option "force_rdirplus" to carry that intent.

Signed-off-by: Benjamin Coddington <bcodding@redhat.com>
---
 fs/nfs/dir.c              |  2 ++
 fs/nfs/fs_context.c       | 14 ++++++++++++--
 include/linux/nfs_fs_sb.h |  1 +
 3 files changed, 15 insertions(+), 2 deletions(-)

Comments

Trond Myklebust March 12, 2025, 10:48 p.m. UTC | #1
On Wed, 2025-03-12 at 15:46 -0400, Benjamin Coddington wrote:
> There are certain users that wish to force the NFS client to choose
> READDIRPLUS over READDIR for a particular mount.  Add a new kernel
> mount
> option "force_rdirplus" to carry that intent.

Could we perhaps convert rdirplus to be a string with an optional
payload? Does the "fs_param_can_be_empty" flag allow you to convert
rdirplus into something that can behave as currently if you just
specify '-ordirplus', but that would allow you to specify '-
ordirplus=force' if you wanted to always use readdirplus?
Benjamin Coddington March 12, 2025, 10:57 p.m. UTC | #2
On 12 Mar 2025, at 18:48, Trond Myklebust wrote:

> On Wed, 2025-03-12 at 15:46 -0400, Benjamin Coddington wrote:
>> There are certain users that wish to force the NFS client to choose
>> READDIRPLUS over READDIR for a particular mount.  Add a new kernel
>> mount
>> option "force_rdirplus" to carry that intent.
>
> Could we perhaps convert rdirplus to be a string with an optional
> payload? Does the "fs_param_can_be_empty" flag allow you to convert
> rdirplus into something that can behave as currently if you just
> specify '-ordirplus', but that would allow you to specify '-
> ordirplus=force' if you wanted to always use readdirplus?

Yes, I think that's possible.  I originally started down that route, but
abandoned it after it appeared to be a bigger code diff because you have to
re-define the nordirplus option which we get for free with fsparam_flag_no.

I can send a v2 that way if it's preferred.

Ben
Trond Myklebust March 12, 2025, 11:03 p.m. UTC | #3
On Wed, 2025-03-12 at 18:57 -0400, Benjamin Coddington wrote:
> On 12 Mar 2025, at 18:48, Trond Myklebust wrote:
> 
> > On Wed, 2025-03-12 at 15:46 -0400, Benjamin Coddington wrote:
> > > There are certain users that wish to force the NFS client to
> > > choose
> > > READDIRPLUS over READDIR for a particular mount.  Add a new
> > > kernel
> > > mount
> > > option "force_rdirplus" to carry that intent.
> > 
> > Could we perhaps convert rdirplus to be a string with an optional
> > payload? Does the "fs_param_can_be_empty" flag allow you to convert
> > rdirplus into something that can behave as currently if you just
> > specify '-ordirplus', but that would allow you to specify '-
> > ordirplus=force' if you wanted to always use readdirplus?
> 
> Yes, I think that's possible.  I originally started down that route,
> but
> abandoned it after it appeared to be a bigger code diff because you
> have to
> re-define the nordirplus option which we get for free with
> fsparam_flag_no.
> 
> I can send a v2 that way if it's preferred.

To me that seems tidier, and easier to remember. It also allows you to
alias nordirplus as a 'rdirplus=none' option, should that be desirable.
kernel test robot March 13, 2025, 2:20 p.m. UTC | #4
Hi Benjamin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 2408a807bfc3f738850ef5ad5e3fd59d66168996]

url:    https://github.com/intel-lab-lkp/linux/commits/Benjamin-Coddington/NFS-New-mount-option-force_rdirplus/20250313-034816
base:   2408a807bfc3f738850ef5ad5e3fd59d66168996
patch link:    https://lore.kernel.org/r/4a471ab1bdea1052f45d894c967d0a6b6e38d4a6.1741806879.git.bcodding%40redhat.com
patch subject: [PATCH 1/1] NFS: New mount option force_rdirplus
config: i386-randconfig-003-20250313 (https://download.01.org/0day-ci/archive/20250313/202503132212.aPAVQklX-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250313/202503132212.aPAVQklX-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503132212.aPAVQklX-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/nfs/fs_context.c:642:19: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
     642 |                         if (ctx->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                        ^  ~~~~~~~~~~~~~~~~~~~~~~~~
   fs/nfs/fs_context.c:642:19: note: use '&' for a bitwise operation
     642 |                         if (ctx->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                        ^~
         |                                        &
   fs/nfs/fs_context.c:642:19: note: remove constant to silence this warning
     642 |                         if (ctx->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/nfs/fs_context.c:650:18: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
     650 |                 if (ctx->flags && NFS_MOUNT_NORDIRPLUS)
         |                                ^  ~~~~~~~~~~~~~~~~~~~~
   fs/nfs/fs_context.c:650:18: note: use '&' for a bitwise operation
     650 |                 if (ctx->flags && NFS_MOUNT_NORDIRPLUS)
         |                                ^~
         |                                &
   fs/nfs/fs_context.c:650:18: note: remove constant to silence this warning
     650 |                 if (ctx->flags && NFS_MOUNT_NORDIRPLUS)
         |                                ^~~~~~~~~~~~~~~~~~~~~~~
   2 warnings generated.
--
>> fs/nfs/dir.c:669:29: warning: use of logical '&&' with constant operand [-Wconstant-logical-operand]
     669 |         if (NFS_SERVER(dir)->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                    ^  ~~~~~~~~~~~~~~~~~~~~~~~~
   fs/nfs/dir.c:669:29: note: use '&' for a bitwise operation
     669 |         if (NFS_SERVER(dir)->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                    ^~
         |                                    &
   fs/nfs/dir.c:669:29: note: remove constant to silence this warning
     669 |         if (NFS_SERVER(dir)->flags && NFS_MOUNT_FORCE_RDIRPLUS)
         |                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   1 warning generated.


vim +642 fs/nfs/fs_context.c

   529	
   530	/*
   531	 * Parse a single mount parameter.
   532	 */
   533	static int nfs_fs_context_parse_param(struct fs_context *fc,
   534					      struct fs_parameter *param)
   535	{
   536		struct fs_parse_result result;
   537		struct nfs_fs_context *ctx = nfs_fc2context(fc);
   538		unsigned short protofamily, mountfamily;
   539		unsigned int len;
   540		int ret, opt;
   541	
   542		trace_nfs_mount_option(param);
   543	
   544		opt = fs_parse(fc, nfs_fs_parameters, param, &result);
   545		if (opt < 0)
   546			return (opt == -ENOPARAM && ctx->sloppy) ? 1 : opt;
   547	
   548		if (fc->security)
   549			ctx->has_sec_mnt_opts = 1;
   550	
   551		switch (opt) {
   552		case Opt_source:
   553			if (fc->source)
   554				return nfs_invalf(fc, "NFS: Multiple sources not supported");
   555			fc->source = param->string;
   556			param->string = NULL;
   557			break;
   558	
   559			/*
   560			 * boolean options:  foo/nofoo
   561			 */
   562		case Opt_soft:
   563			ctx->flags |= NFS_MOUNT_SOFT;
   564			ctx->flags &= ~NFS_MOUNT_SOFTERR;
   565			break;
   566		case Opt_softerr:
   567			ctx->flags |= NFS_MOUNT_SOFTERR | NFS_MOUNT_SOFTREVAL;
   568			ctx->flags &= ~NFS_MOUNT_SOFT;
   569			break;
   570		case Opt_hard:
   571			ctx->flags &= ~(NFS_MOUNT_SOFT |
   572					NFS_MOUNT_SOFTERR |
   573					NFS_MOUNT_SOFTREVAL);
   574			break;
   575		case Opt_softreval:
   576			if (result.negated)
   577				ctx->flags &= ~NFS_MOUNT_SOFTREVAL;
   578			else
   579				ctx->flags |= NFS_MOUNT_SOFTREVAL;
   580			break;
   581		case Opt_posix:
   582			if (result.negated)
   583				ctx->flags &= ~NFS_MOUNT_POSIX;
   584			else
   585				ctx->flags |= NFS_MOUNT_POSIX;
   586			break;
   587		case Opt_cto:
   588			if (result.negated)
   589				ctx->flags |= NFS_MOUNT_NOCTO;
   590			else
   591				ctx->flags &= ~NFS_MOUNT_NOCTO;
   592			break;
   593		case Opt_trunkdiscovery:
   594			if (result.negated)
   595				ctx->flags &= ~NFS_MOUNT_TRUNK_DISCOVERY;
   596			else
   597				ctx->flags |= NFS_MOUNT_TRUNK_DISCOVERY;
   598			break;
   599		case Opt_alignwrite:
   600			if (result.negated)
   601				ctx->flags |= NFS_MOUNT_NO_ALIGNWRITE;
   602			else
   603				ctx->flags &= ~NFS_MOUNT_NO_ALIGNWRITE;
   604			break;
   605		case Opt_ac:
   606			if (result.negated)
   607				ctx->flags |= NFS_MOUNT_NOAC;
   608			else
   609				ctx->flags &= ~NFS_MOUNT_NOAC;
   610			break;
   611		case Opt_lock:
   612			if (result.negated) {
   613				ctx->lock_status = NFS_LOCK_NOLOCK;
   614				ctx->flags |= NFS_MOUNT_NONLM;
   615				ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
   616			} else {
   617				ctx->lock_status = NFS_LOCK_LOCK;
   618				ctx->flags &= ~NFS_MOUNT_NONLM;
   619				ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
   620			}
   621			break;
   622		case Opt_udp:
   623			ctx->flags &= ~NFS_MOUNT_TCP;
   624			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
   625			break;
   626		case Opt_tcp:
   627		case Opt_rdma:
   628			ctx->flags |= NFS_MOUNT_TCP; /* for side protocols */
   629			ret = xprt_find_transport_ident(param->key);
   630			if (ret < 0)
   631				goto out_bad_transport;
   632			ctx->nfs_server.protocol = ret;
   633			break;
   634		case Opt_acl:
   635			if (result.negated)
   636				ctx->flags |= NFS_MOUNT_NOACL;
   637			else
   638				ctx->flags &= ~NFS_MOUNT_NOACL;
   639			break;
   640		case Opt_rdirplus:
   641			if (result.negated) {
 > 642				if (ctx->flags && NFS_MOUNT_FORCE_RDIRPLUS)
   643					return nfs_invalf(fc, "NFS: Cannot both force and disable READDIR PLUS");
   644				ctx->flags |= NFS_MOUNT_NORDIRPLUS;
   645			} else {
   646				ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
   647			}
   648			break;
   649		case Opt_force_rdirplus:
   650			if (ctx->flags && NFS_MOUNT_NORDIRPLUS)
   651				return nfs_invalf(fc, "NFS: Cannot both force and disable READDIR PLUS");
   652			ctx->flags |= NFS_MOUNT_FORCE_RDIRPLUS;
   653			break;
   654		case Opt_sharecache:
   655			if (result.negated)
   656				ctx->flags |= NFS_MOUNT_UNSHARED;
   657			else
   658				ctx->flags &= ~NFS_MOUNT_UNSHARED;
   659			break;
   660		case Opt_resvport:
   661			if (result.negated)
   662				ctx->flags |= NFS_MOUNT_NORESVPORT;
   663			else
   664				ctx->flags &= ~NFS_MOUNT_NORESVPORT;
   665			break;
   666		case Opt_fscache_flag:
   667			if (result.negated)
   668				ctx->options &= ~NFS_OPTION_FSCACHE;
   669			else
   670				ctx->options |= NFS_OPTION_FSCACHE;
   671			kfree(ctx->fscache_uniq);
   672			ctx->fscache_uniq = NULL;
   673			break;
   674		case Opt_fscache:
   675			trace_nfs_mount_assign(param->key, param->string);
   676			ctx->options |= NFS_OPTION_FSCACHE;
   677			kfree(ctx->fscache_uniq);
   678			ctx->fscache_uniq = param->string;
   679			param->string = NULL;
   680			break;
   681		case Opt_migration:
   682			if (result.negated)
   683				ctx->options &= ~NFS_OPTION_MIGRATION;
   684			else
   685				ctx->options |= NFS_OPTION_MIGRATION;
   686			break;
   687	
   688			/*
   689			 * options that take numeric values
   690			 */
   691		case Opt_port:
   692			if (result.uint_32 > USHRT_MAX)
   693				goto out_of_bounds;
   694			ctx->nfs_server.port = result.uint_32;
   695			break;
   696		case Opt_rsize:
   697			ctx->rsize = result.uint_32;
   698			break;
   699		case Opt_wsize:
   700			ctx->wsize = result.uint_32;
   701			break;
   702		case Opt_bsize:
   703			ctx->bsize = result.uint_32;
   704			break;
   705		case Opt_timeo:
   706			if (result.uint_32 < 1 || result.uint_32 > INT_MAX)
   707				goto out_of_bounds;
   708			ctx->timeo = result.uint_32;
   709			break;
   710		case Opt_retrans:
   711			if (result.uint_32 > INT_MAX)
   712				goto out_of_bounds;
   713			ctx->retrans = result.uint_32;
   714			break;
   715		case Opt_acregmin:
   716			ctx->acregmin = result.uint_32;
   717			break;
   718		case Opt_acregmax:
   719			ctx->acregmax = result.uint_32;
   720			break;
   721		case Opt_acdirmin:
   722			ctx->acdirmin = result.uint_32;
   723			break;
   724		case Opt_acdirmax:
   725			ctx->acdirmax = result.uint_32;
   726			break;
   727		case Opt_actimeo:
   728			ctx->acregmin = result.uint_32;
   729			ctx->acregmax = result.uint_32;
   730			ctx->acdirmin = result.uint_32;
   731			ctx->acdirmax = result.uint_32;
   732			break;
   733		case Opt_namelen:
   734			ctx->namlen = result.uint_32;
   735			break;
   736		case Opt_mountport:
   737			if (result.uint_32 > USHRT_MAX)
   738				goto out_of_bounds;
   739			ctx->mount_server.port = result.uint_32;
   740			break;
   741		case Opt_mountvers:
   742			if (result.uint_32 < NFS_MNT_VERSION ||
   743			    result.uint_32 > NFS_MNT3_VERSION)
   744				goto out_of_bounds;
   745			ctx->mount_server.version = result.uint_32;
   746			break;
   747		case Opt_minorversion:
   748			if (result.uint_32 > NFS4_MAX_MINOR_VERSION)
   749				goto out_of_bounds;
   750			ctx->minorversion = result.uint_32;
   751			break;
   752	
   753			/*
   754			 * options that take text values
   755			 */
   756		case Opt_v:
   757			ret = nfs_parse_version_string(fc, param->key + 1);
   758			if (ret < 0)
   759				return ret;
   760			break;
   761		case Opt_vers:
   762			if (!param->string)
   763				goto out_invalid_value;
   764			trace_nfs_mount_assign(param->key, param->string);
   765			ret = nfs_parse_version_string(fc, param->string);
   766			if (ret < 0)
   767				return ret;
   768			break;
   769		case Opt_sec:
   770			ret = nfs_parse_security_flavors(fc, param);
   771			if (ret < 0)
   772				return ret;
   773			break;
   774		case Opt_xprtsec:
   775			ret = nfs_parse_xprtsec_policy(fc, param);
   776			if (ret < 0)
   777				return ret;
   778			break;
   779	
   780		case Opt_proto:
   781			if (!param->string)
   782				goto out_invalid_value;
   783			trace_nfs_mount_assign(param->key, param->string);
   784			protofamily = AF_INET;
   785			switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
   786			case Opt_xprt_udp6:
   787				protofamily = AF_INET6;
   788				fallthrough;
   789			case Opt_xprt_udp:
   790				ctx->flags &= ~NFS_MOUNT_TCP;
   791				ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
   792				break;
   793			case Opt_xprt_tcp6:
   794				protofamily = AF_INET6;
   795				fallthrough;
   796			case Opt_xprt_tcp:
   797				ctx->flags |= NFS_MOUNT_TCP;
   798				ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
   799				break;
   800			case Opt_xprt_rdma6:
   801				protofamily = AF_INET6;
   802				fallthrough;
   803			case Opt_xprt_rdma:
   804				/* vector side protocols to TCP */
   805				ctx->flags |= NFS_MOUNT_TCP;
   806				ret = xprt_find_transport_ident(param->string);
   807				if (ret < 0)
   808					goto out_bad_transport;
   809				ctx->nfs_server.protocol = ret;
   810				break;
   811			default:
   812				goto out_bad_transport;
   813			}
   814	
   815			ctx->protofamily = protofamily;
   816			break;
   817	
   818		case Opt_mountproto:
   819			if (!param->string)
   820				goto out_invalid_value;
   821			trace_nfs_mount_assign(param->key, param->string);
   822			mountfamily = AF_INET;
   823			switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
   824			case Opt_xprt_udp6:
   825				mountfamily = AF_INET6;
   826				fallthrough;
   827			case Opt_xprt_udp:
   828				ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
   829				break;
   830			case Opt_xprt_tcp6:
   831				mountfamily = AF_INET6;
   832				fallthrough;
   833			case Opt_xprt_tcp:
   834				ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
   835				break;
   836			case Opt_xprt_rdma: /* not used for side protocols */
   837			default:
   838				goto out_bad_transport;
   839			}
   840			ctx->mountfamily = mountfamily;
   841			break;
   842	
   843		case Opt_addr:
   844			trace_nfs_mount_assign(param->key, param->string);
   845			len = rpc_pton(fc->net_ns, param->string, param->size,
   846				       &ctx->nfs_server.address,
   847				       sizeof(ctx->nfs_server._address));
   848			if (len == 0)
   849				goto out_invalid_address;
   850			ctx->nfs_server.addrlen = len;
   851			break;
   852		case Opt_clientaddr:
   853			trace_nfs_mount_assign(param->key, param->string);
   854			kfree(ctx->client_address);
   855			ctx->client_address = param->string;
   856			param->string = NULL;
   857			break;
   858		case Opt_mounthost:
   859			trace_nfs_mount_assign(param->key, param->string);
   860			kfree(ctx->mount_server.hostname);
   861			ctx->mount_server.hostname = param->string;
   862			param->string = NULL;
   863			break;
   864		case Opt_mountaddr:
   865			trace_nfs_mount_assign(param->key, param->string);
   866			len = rpc_pton(fc->net_ns, param->string, param->size,
   867				       &ctx->mount_server.address,
   868				       sizeof(ctx->mount_server._address));
   869			if (len == 0)
   870				goto out_invalid_address;
   871			ctx->mount_server.addrlen = len;
   872			break;
   873		case Opt_nconnect:
   874			trace_nfs_mount_assign(param->key, param->string);
   875			if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_CONNECTIONS)
   876				goto out_of_bounds;
   877			ctx->nfs_server.nconnect = result.uint_32;
   878			break;
   879		case Opt_max_connect:
   880			trace_nfs_mount_assign(param->key, param->string);
   881			if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_TRANSPORTS)
   882				goto out_of_bounds;
   883			ctx->nfs_server.max_connect = result.uint_32;
   884			break;
   885		case Opt_lookupcache:
   886			trace_nfs_mount_assign(param->key, param->string);
   887			switch (result.uint_32) {
   888			case Opt_lookupcache_all:
   889				ctx->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
   890				break;
   891			case Opt_lookupcache_positive:
   892				ctx->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
   893				ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
   894				break;
   895			case Opt_lookupcache_none:
   896				ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
   897				break;
   898			default:
   899				goto out_invalid_value;
   900			}
   901			break;
   902		case Opt_local_lock:
   903			trace_nfs_mount_assign(param->key, param->string);
   904			switch (result.uint_32) {
   905			case Opt_local_lock_all:
   906				ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK |
   907					       NFS_MOUNT_LOCAL_FCNTL);
   908				break;
   909			case Opt_local_lock_flock:
   910				ctx->flags |= NFS_MOUNT_LOCAL_FLOCK;
   911				break;
   912			case Opt_local_lock_posix:
   913				ctx->flags |= NFS_MOUNT_LOCAL_FCNTL;
   914				break;
   915			case Opt_local_lock_none:
   916				ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
   917						NFS_MOUNT_LOCAL_FCNTL);
   918				break;
   919			default:
   920				goto out_invalid_value;
   921			}
   922			break;
   923		case Opt_write:
   924			trace_nfs_mount_assign(param->key, param->string);
   925			switch (result.uint_32) {
   926			case Opt_write_lazy:
   927				ctx->flags &=
   928					~(NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT);
   929				break;
   930			case Opt_write_eager:
   931				ctx->flags |= NFS_MOUNT_WRITE_EAGER;
   932				ctx->flags &= ~NFS_MOUNT_WRITE_WAIT;
   933				break;
   934			case Opt_write_wait:
   935				ctx->flags |=
   936					NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT;
   937				break;
   938			default:
   939				goto out_invalid_value;
   940			}
   941			break;
   942	
   943			/*
   944			 * Special options
   945			 */
   946		case Opt_sloppy:
   947			ctx->sloppy = true;
   948			break;
   949		}
   950	
   951		return 0;
   952	
   953	out_invalid_value:
   954		return nfs_invalf(fc, "NFS: Bad mount option value specified");
   955	out_invalid_address:
   956		return nfs_invalf(fc, "NFS: Bad IP address specified");
   957	out_of_bounds:
   958		return nfs_invalf(fc, "NFS: Value for '%s' out of range", param->key);
   959	out_bad_transport:
   960		return nfs_invalf(fc, "NFS: Unrecognized transport protocol");
   961	}
   962
diff mbox series

Patch

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 2b04038b0e40..c9de0e474cf5 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -666,6 +666,8 @@  static bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx,
 {
 	if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
 		return false;
+	if (NFS_SERVER(dir)->flags && NFS_MOUNT_FORCE_RDIRPLUS)
+		return true;
 	if (ctx->pos == 0 ||
 	    cache_hits + cache_misses > NFS_READDIR_CACHE_USAGE_THRESHOLD)
 		return true;
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index b069385eea17..3ba44b444031 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -72,6 +72,7 @@  enum nfs_param {
 	Opt_posix,
 	Opt_proto,
 	Opt_rdirplus,
+	Opt_force_rdirplus,
 	Opt_rdma,
 	Opt_resvport,
 	Opt_retrans,
@@ -175,6 +176,7 @@  static const struct fs_parameter_spec nfs_fs_parameters[] = {
 	fsparam_flag_no("posix",	Opt_posix),
 	fsparam_string("proto",		Opt_proto),
 	fsparam_flag_no("rdirplus",	Opt_rdirplus),
+	fsparam_flag  ("force_rdirplus", Opt_force_rdirplus),
 	fsparam_flag  ("rdma",		Opt_rdma),
 	fsparam_flag_no("resvport",	Opt_resvport),
 	fsparam_u32   ("retrans",	Opt_retrans),
@@ -636,10 +638,18 @@  static int nfs_fs_context_parse_param(struct fs_context *fc,
 			ctx->flags &= ~NFS_MOUNT_NOACL;
 		break;
 	case Opt_rdirplus:
-		if (result.negated)
+		if (result.negated) {
+			if (ctx->flags && NFS_MOUNT_FORCE_RDIRPLUS)
+				return nfs_invalf(fc, "NFS: Cannot both force and disable READDIR PLUS");
 			ctx->flags |= NFS_MOUNT_NORDIRPLUS;
-		else
+		} else {
 			ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
+		}
+		break;
+	case Opt_force_rdirplus:
+		if (ctx->flags && NFS_MOUNT_NORDIRPLUS)
+			return nfs_invalf(fc, "NFS: Cannot both force and disable READDIR PLUS");
+		ctx->flags |= NFS_MOUNT_FORCE_RDIRPLUS;
 		break;
 	case Opt_sharecache:
 		if (result.negated)
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index f00bfcee7120..3774b2235a1e 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -167,6 +167,7 @@  struct nfs_server {
 #define NFS_MOUNT_TRUNK_DISCOVERY	0x04000000
 #define NFS_MOUNT_SHUTDOWN			0x08000000
 #define NFS_MOUNT_NO_ALIGNWRITE		0x10000000
+#define NFS_MOUNT_FORCE_RDIRPLUS	0x20000000
 
 	unsigned int		fattr_valid;	/* Valid attributes */
 	unsigned int		caps;		/* server capabilities */