forked from Archive/PX4-Autopilot
NFS code shrinking
git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@4833 7fd9a85b-ad96-42d3-883c-3090e2eb8679
This commit is contained in:
parent
e7b80154df
commit
52ae543656
|
@ -63,14 +63,9 @@
|
||||||
#define NFS_TIMEOUTMUL 2 /* Timeout/Delay multiplier */
|
#define NFS_TIMEOUTMUL 2 /* Timeout/Delay multiplier */
|
||||||
#define NFS_MAXREXMIT 100 /* Stop counting after this many */
|
#define NFS_MAXREXMIT 100 /* Stop counting after this many */
|
||||||
#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
|
#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
|
||||||
#define NFS_MAXGRPS 16 /* Max. size of groups list */
|
|
||||||
#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
|
|
||||||
#define NFS_MAXATTRTIMO 60
|
|
||||||
#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
|
#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
|
||||||
#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
|
#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
|
||||||
#define NFS_READDIRSIZE 8192 /* Def. readdir size */
|
#define NFS_READDIRSIZE 8192 /* Def. readdir size */
|
||||||
#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
|
|
||||||
#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
|
|
||||||
#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
|
#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
|
||||||
#define NFS_NPROCS 23
|
#define NFS_NPROCS 23
|
||||||
|
|
||||||
|
@ -118,21 +113,6 @@
|
||||||
#define NFSSVC_AUTHINFAIL 0x080
|
#define NFSSVC_AUTHINFAIL 0x080
|
||||||
#define NFSSVC_MNTD 0x100
|
#define NFSSVC_MNTD 0x100
|
||||||
|
|
||||||
/* fs.nfs sysctl(3) identifiers */
|
|
||||||
|
|
||||||
#define NFS_NFSSTATS 1 /* struct: struct nfsstats */
|
|
||||||
#define NFS_NIOTHREADS 2 /* number of i/o threads */
|
|
||||||
#define NFS_MAXID 3
|
|
||||||
|
|
||||||
#define FS_NFS_NAMES { \
|
|
||||||
{ 0, 0 }, \
|
|
||||||
{ "nfsstats", CTLTYPE_STRUCT }, \
|
|
||||||
{ "iothreads", CTLTYPE_INT } \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
|
|
||||||
sigmask(SIGHUP)|sigmask(SIGQUIT))
|
|
||||||
|
|
||||||
/* Socket errors ignored for connectionless sockets??
|
/* Socket errors ignored for connectionless sockets??
|
||||||
* For now, ignore them all
|
* For now, ignore them all
|
||||||
*/
|
*/
|
||||||
|
@ -193,6 +173,14 @@
|
||||||
#define ND_NFSV3 0x08
|
#define ND_NFSV3 0x08
|
||||||
#define NFSD_CHECKSLP 0x01
|
#define NFSD_CHECKSLP 0x01
|
||||||
|
|
||||||
|
/* Increment NFS statistics */
|
||||||
|
|
||||||
|
#ifdef CONFIG_NFS_STATISTICS
|
||||||
|
# define nfs_statistics(n) do { nfsstats.rpccnt[n]++; } while (0)
|
||||||
|
#else
|
||||||
|
# define nfs_statistics(n)
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Data
|
* Public Data
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -200,7 +188,9 @@
|
||||||
extern uint32_t nfs_true;
|
extern uint32_t nfs_true;
|
||||||
extern uint32_t nfs_false;
|
extern uint32_t nfs_false;
|
||||||
extern uint32_t nfs_xdrneg1;
|
extern uint32_t nfs_xdrneg1;
|
||||||
|
#ifdef CONFIG_NFS_STATISTICS
|
||||||
extern struct nfsstats nfsstats;
|
extern struct nfsstats nfsstats;
|
||||||
|
#endif
|
||||||
extern int nfs_ticks;
|
extern int nfs_ticks;
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
@ -309,40 +299,7 @@ struct nfsrv_descript
|
||||||
|
|
||||||
struct nfsstats
|
struct nfsstats
|
||||||
{
|
{
|
||||||
uint64_t attrcache_hits;
|
|
||||||
uint64_t attrcache_misses;
|
|
||||||
uint64_t lookupcache_hits;
|
|
||||||
uint64_t lookupcache_misses;
|
|
||||||
uint64_t direofcache_hits;
|
|
||||||
uint64_t direofcache_misses;
|
|
||||||
uint64_t biocache_reads;
|
|
||||||
uint64_t read_bios;
|
|
||||||
uint64_t read_physios;
|
|
||||||
uint64_t biocache_writes;
|
|
||||||
uint64_t write_bios;
|
|
||||||
uint64_t write_physios;
|
|
||||||
uint64_t biocache_readlinks;
|
|
||||||
uint64_t readlink_bios;
|
|
||||||
uint64_t biocache_readdirs;
|
|
||||||
uint64_t readdir_bios;
|
|
||||||
uint64_t rpccnt[NFS_NPROCS];
|
uint64_t rpccnt[NFS_NPROCS];
|
||||||
uint64_t rpcretries;
|
|
||||||
uint64_t srvrpccnt[NFS_NPROCS];
|
|
||||||
uint64_t srvrpc_errs;
|
|
||||||
uint64_t srv_errs;
|
|
||||||
uint64_t rpcrequests;
|
|
||||||
uint64_t rpctimeouts;
|
|
||||||
uint64_t rpcunexpected;
|
|
||||||
uint64_t rpcinvalid;
|
|
||||||
uint64_t srvcache_inproghits;
|
|
||||||
uint64_t srvcache_idemdonehits;
|
|
||||||
uint64_t srvcache_nonidemdonehits;
|
|
||||||
uint64_t srvcache_misses;
|
|
||||||
uint64_t forcedsync;
|
|
||||||
uint64_t srvnqnfs_leases;
|
|
||||||
uint64_t srvnqnfs_maxleases;
|
|
||||||
uint64_t srvnqnfs_getleases;
|
|
||||||
uint64_t srvvop_writes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
@ -352,6 +309,7 @@ struct nfsstats
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Function Prototypes
|
* Public Function Prototypes
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
#define EXTERN extern "C"
|
#define EXTERN extern "C"
|
||||||
|
@ -377,6 +335,7 @@ EXTERN int nfs_finddir(FAR struct nfsmount *nmp, FAR const char *relpath,
|
||||||
FAR struct nfs_fattr *attributes, FAR char *filename);
|
FAR struct nfs_fattr *attributes, FAR char *filename);
|
||||||
EXTERN void nfs_attrupdate(FAR struct nfsnode *np,
|
EXTERN void nfs_attrupdate(FAR struct nfsnode *np,
|
||||||
FAR struct nfs_fattr *attributes);
|
FAR struct nfs_fattr *attributes);
|
||||||
|
|
||||||
#undef EXTERN
|
#undef EXTERN
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
||||||
* Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
|
* Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
|
||||||
* Author: Jose Pablo Rojas Vargas <jrojas@nx-engineering.com>
|
* Author: Jose Pablo Rojas Vargas <jrojas@nx-engineering.com>
|
||||||
|
* Gregory Nutt <gnutt@nuttx.org>
|
||||||
*
|
*
|
||||||
* Leveraged from OpenBSD:
|
* Leveraged from OpenBSD:
|
||||||
*
|
*
|
||||||
|
@ -58,45 +59,30 @@
|
||||||
* Public Types
|
* Public Types
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/* Mount structure.
|
/* Mount structure. One mount structure is allocated for each NFS mount. This
|
||||||
* One allocated on every NFS mount.
|
* structure holds NFS specific information for mount.
|
||||||
* Holds NFS specific information for mount.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct nfsmount
|
struct nfsmount
|
||||||
{
|
{
|
||||||
uint32_t nm_flag; /* Flags for soft/hard... */
|
struct nfsnode *nm_head; /* A list of all files opened on this mountpoint */
|
||||||
uint32_t nm_state; /* Internal state flags */
|
sem_t nm_sem; /* Used to assure thread-safe access */
|
||||||
struct nfsnode *nm_head; /* A list to all files opened on this mountpoint */
|
|
||||||
bool nm_mounted; /* true: The file system is ready */
|
|
||||||
sem_t nm_sem; /* Used to assume thread-safe access */
|
|
||||||
uint32_t nm_numgrps; /* Max. size of groupslist */
|
|
||||||
nfsfh_t nm_fh; /* File handle of root dir */
|
nfsfh_t nm_fh; /* File handle of root dir */
|
||||||
char nm_path[90]; /* server's path of the directory being mount */
|
char nm_path[90]; /* server's path of the directory being mounted */
|
||||||
uint32_t nm_fhsize; /* Size of root file handle (host order) */
|
|
||||||
struct nfs_fattr nm_fattr; /* nfs file attribute cache */
|
struct nfs_fattr nm_fattr; /* nfs file attribute cache */
|
||||||
struct rpcclnt *nm_rpcclnt; /* rpc state */
|
struct rpcclnt *nm_rpcclnt; /* RPC state */
|
||||||
struct socket *nm_so; /* Rpc socket */
|
struct socket *nm_so; /* RPC socket */
|
||||||
|
struct sockaddr nm_nam; /* Addr of server */
|
||||||
|
bool nm_mounted; /* true: The file system is ready */
|
||||||
|
uint8_t nm_flag; /* Flags for soft/hard... */
|
||||||
|
uint8_t nm_fhsize; /* Size of root file handle (host order) */
|
||||||
uint8_t nm_sotype; /* Type of socket */
|
uint8_t nm_sotype; /* Type of socket */
|
||||||
uint8_t nm_soproto; /* and protocol */
|
uint8_t nm_soproto; /* and protocol */
|
||||||
uint8_t nm_soflags; /* pr_flags for socket protocol */
|
uint8_t nm_timeo; /* Init timer for NFSMNT_DUMBTIMR */
|
||||||
struct sockaddr nm_nam; /* Addr of server */
|
uint8_t nm_retry; /* Max retries */
|
||||||
uint32_t nm_timeo; /* Init timer for NFSMNT_DUMBTIMR */
|
uint16_t nm_rsize; /* Max size of read RPC */
|
||||||
uint32_t nm_retry; /* Max retries */
|
uint16_t nm_wsize; /* Max size of write RPC */
|
||||||
uint32_t nm_srtt[4]; /* Timers for rpcs */
|
uint16_t nm_readdirsize; /* Size of a readdir RPC */
|
||||||
uint32_t nm_sdrtt[4];
|
|
||||||
uint32_t nm_sent; /* Request send count */
|
|
||||||
uint32_t nm_cwnd; /* Request send window */
|
|
||||||
uint32_t nm_timeouts; /* Request timeouts */
|
|
||||||
//uint32_t nm_deadthresh; /* Threshold of timeouts-->dead server */
|
|
||||||
uint32_t nm_rsize; /* Max size of read rpc */
|
|
||||||
uint32_t nm_wsize; /* Max size of write rpc */
|
|
||||||
uint32_t nm_readdirsize; /* Size of a readdir rpc */
|
|
||||||
uint32_t nm_readahead; /* Num. of blocks to readahead */
|
|
||||||
uint32_t nm_acdirmin; /* Directory attr cache min lifetime */
|
|
||||||
uint32_t nm_acdirmax; /* Directory attr cache max lifetime */
|
|
||||||
uint32_t nm_acregmin; /* Reg file attr cache min lifetime */
|
|
||||||
uint32_t nm_acregmax; /* Reg file attr cache max lifetime */
|
|
||||||
uint8_t nm_verf[NFSX_V3WRITEVERF]; /* V3 write verifier */
|
uint8_t nm_verf[NFSX_V3WRITEVERF]; /* V3 write verifier */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -214,16 +214,16 @@
|
||||||
#define NFSV3FSINFO_HOMOGENEOUS 0x08
|
#define NFSV3FSINFO_HOMOGENEOUS 0x08
|
||||||
#define NFSV3FSINFO_CANSETTIME 0x10
|
#define NFSV3FSINFO_CANSETTIME 0x10
|
||||||
|
|
||||||
/* NFS mount option flags */
|
/* NFS mount option flags (currently fit in a uint8_t) */
|
||||||
|
|
||||||
#define NFSMNT_SOFT 0x00000001 /* soft mount (hard is default) */
|
#define NFSMNT_SOFT (1 << 0) /* soft mount (hard is default) */
|
||||||
#define NFSMNT_WSIZE 0x00000002 /* set write size */
|
#define NFSMNT_WSIZE (1 << 1) /* set write size */
|
||||||
#define NFSMNT_RSIZE 0x00000004 /* set read size */
|
#define NFSMNT_RSIZE (1 << 2) /* set read size */
|
||||||
#define NFSMNT_TIMEO 0x00000008 /* set initial timeout */
|
#define NFSMNT_TIMEO (1 << 3) /* set initial timeout */
|
||||||
#define NFSMNT_RETRANS 0x00000010 /* set number of request retries */
|
#define NFSMNT_RETRANS (1 << 4) /* set number of request retries */
|
||||||
#define NFSMNT_MAXGRPS 0x00000020 /* set maximum grouplist size */
|
#define NFSMNT_MAXGRPS (1 << 5) /* set maximum grouplist size */
|
||||||
#define NFSMNT_INT 0x00000040 /* allow interrupts on hard mount */
|
#define NFSMNT_INT (1 << 6) /* allow interrupts on hard mount */
|
||||||
#define NFSMNT_NOCONN 0x00000080 /* Don't Connect the socket */
|
#define NFSMNT_NOCONN (1 << 7) /* Don't Connect the socket */
|
||||||
|
|
||||||
/* 0x100 free, was NFSMNT_NQNFS */
|
/* 0x100 free, was NFSMNT_NQNFS */
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,7 @@ int nfs_connect(struct nfsmount *nmp)
|
||||||
|
|
||||||
fvdbg("Connecting\n");
|
fvdbg("Connecting\n");
|
||||||
|
|
||||||
/* translate nfsmnt flags -> rpcclnt flags */
|
/* Translate nfsmnt flags -> rpcclnt flags */
|
||||||
|
|
||||||
rpc->rc_flag = 0;
|
rpc->rc_flag = 0;
|
||||||
nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, SOFT);
|
nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, SOFT);
|
||||||
|
@ -147,13 +147,10 @@ int nfs_connect(struct nfsmount *nmp)
|
||||||
|
|
||||||
rpc->rc_sotype = nmp->nm_sotype;
|
rpc->rc_sotype = nmp->nm_sotype;
|
||||||
rpc->rc_soproto = nmp->nm_soproto;
|
rpc->rc_soproto = nmp->nm_soproto;
|
||||||
rpc->rc_rsize = (nmp->nm_rsize > nmp->nm_readdirsize) ? nmp->nm_rsize : nmp->nm_readdirsize;
|
|
||||||
rpc->rc_wsize = nmp->nm_wsize;
|
|
||||||
//rpc->rc_deadthresh = nmp->nm_deadthresh;
|
|
||||||
rpc->rc_timeo = nmp->nm_timeo;
|
rpc->rc_timeo = nmp->nm_timeo;
|
||||||
rpc->rc_retry = nmp->nm_retry;
|
rpc->rc_retry = nmp->nm_retry;
|
||||||
|
|
||||||
/* v3 needs to use this */
|
/* V3 needs to use this */
|
||||||
|
|
||||||
rpc->rc_proctlen = 0;
|
rpc->rc_proctlen = 0;
|
||||||
rpc->rc_proct = NULL;
|
rpc->rc_proct = NULL;
|
||||||
|
@ -187,16 +184,13 @@ int nfs_request(struct nfsmount *nmp, int procnum,
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
tryagain:
|
tryagain:
|
||||||
|
|
||||||
memset(&replyh, 0, sizeof(struct nfs_reply_header));
|
|
||||||
|
|
||||||
error = rpcclnt_request(clnt, procnum, nmp->nm_rpcclnt->rc_prog->prog_id,
|
error = rpcclnt_request(clnt, procnum, nmp->nm_rpcclnt->rc_prog->prog_id,
|
||||||
nmp->nm_rpcclnt->rc_prog->prog_version, request, reqlen,
|
nmp->nm_rpcclnt->rc_prog->prog_version, request, reqlen,
|
||||||
response, resplen);
|
response, resplen);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
{
|
{
|
||||||
fdbg("ERROR: rpcclnt_request failed: %d\n", error);
|
fdbg("ERROR: rpcclnt_request failed: %d\n", error);
|
||||||
goto out;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&replyh, response, sizeof(struct nfs_reply_header));
|
memcpy(&replyh, response, sizeof(struct nfs_reply_header));
|
||||||
|
@ -214,7 +208,7 @@ tryagain:
|
||||||
error = fxdr_unsigned(uint32_t, replyh.nfs_status);
|
error = fxdr_unsigned(uint32_t, replyh.nfs_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
goto out;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (replyh.rpc_verfi.authtype != 0)
|
if (replyh.rpc_verfi.authtype != 0)
|
||||||
|
@ -233,8 +227,8 @@ tryagain:
|
||||||
goto tryagain;
|
goto tryagain;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the File Handle was stale, invalidate the
|
/* Check for a stale file handle. We don't do anything special
|
||||||
* lookup cache, just in case.
|
* a stale handle other than report a special debug error message.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (error == ESTALE)
|
if (error == ESTALE)
|
||||||
|
@ -248,23 +242,20 @@ tryagain:
|
||||||
nmp->nm_rpcclnt->rc_prog->prog_name, error);
|
nmp->nm_rpcclnt->rc_prog->prog_name, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
goto out;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
fvdbg("NFS_SUCCESS\n");
|
fvdbg("NFS_SUCCESS\n");
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
out:
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef COMP
|
#undef COMP
|
||||||
#ifdef COMP
|
#ifdef COMP
|
||||||
|
|
||||||
/* terminate any outstanding RPCs. */
|
/* Terminate any outstanding RPCs. */
|
||||||
|
|
||||||
int nfs_nmcancelreqs(struct nfsmount *nmp)
|
int nfs_nmcancelreqs(struct nfsmount *nmp)
|
||||||
{
|
{
|
||||||
return 0; //rpcclnt_cancelreqs(nmp->nm_rpcclnt);
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* fs/nfs/nfs_util.h
|
* fs/nfs/nfs_util.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
||||||
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
|
||||||
|
@ -221,12 +221,12 @@ int nfs_fsinfo(FAR struct nfsmount *nmp)
|
||||||
memset(&fsinfo, 0, sizeof(struct FS3args));
|
memset(&fsinfo, 0, sizeof(struct FS3args));
|
||||||
memset(&fsp, 0, sizeof(struct rpc_reply_fsinfo));
|
memset(&fsp, 0, sizeof(struct rpc_reply_fsinfo));
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_FSINFO]++;
|
|
||||||
fsinfo.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
|
fsinfo.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
|
||||||
fsinfo.fsroot.handle = nmp->nm_fh;
|
fsinfo.fsroot.handle = nmp->nm_fh;
|
||||||
|
|
||||||
/* Request FSINFO from the server */
|
/* Request FSINFO from the server */
|
||||||
|
|
||||||
|
nfs_statistics(NFSPROC_FSINFO);
|
||||||
error = nfs_request(nmp, NFSPROC_FSINFO,
|
error = nfs_request(nmp, NFSPROC_FSINFO,
|
||||||
(FAR const void *)&fsinfo, sizeof(struct FS3args),
|
(FAR const void *)&fsinfo, sizeof(struct FS3args),
|
||||||
(FAR void *)&fsp, sizeof(struct rpc_reply_fsinfo));
|
(FAR void *)&fsp, sizeof(struct rpc_reply_fsinfo));
|
||||||
|
@ -359,7 +359,7 @@ int nfs_lookup(struct nfsmount *nmp, FAR const char *filename,
|
||||||
|
|
||||||
/* Request LOOKUP from the server */
|
/* Request LOOKUP from the server */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_LOOKUP]++;
|
nfs_statistics(NFSPROC_LOOKUP);
|
||||||
error = nfs_request(nmp, NFSPROC_LOOKUP,
|
error = nfs_request(nmp, NFSPROC_LOOKUP,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)&response, sizeof(struct rpc_reply_lookup));
|
(FAR void *)&response, sizeof(struct rpc_reply_lookup));
|
||||||
|
|
|
@ -313,7 +313,7 @@ static int nfs_filecreate(FAR struct nfsmount *nmp, struct nfsnode *np,
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
nfsstats.rpccnt[NFSPROC_CREATE]++;
|
nfs_statistics(NFSPROC_CREATE);
|
||||||
error = nfs_request(nmp, NFSPROC_CREATE,
|
error = nfs_request(nmp, NFSPROC_CREATE,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_create));
|
(FAR void *)&resok, sizeof(struct rpc_reply_create));
|
||||||
|
@ -767,7 +767,7 @@ static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen)
|
||||||
/* Perform the read */
|
/* Perform the read */
|
||||||
|
|
||||||
fvdbg("Reading %d bytes\n", readsize);
|
fvdbg("Reading %d bytes\n", readsize);
|
||||||
nfsstats.rpccnt[NFSPROC_READ]++;
|
nfs_statistics(NFSPROC_READ);
|
||||||
error = nfs_request(nmp, NFSPROC_READ,
|
error = nfs_request(nmp, NFSPROC_READ,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)np->n_iobuffer, np->n_buflen);
|
(FAR void *)np->n_iobuffer, np->n_buflen);
|
||||||
|
@ -947,11 +947,11 @@ static ssize_t nfs_write(FAR struct file *filep, const char *buffer,
|
||||||
*ptr++ = txdr_unsigned(buflen);
|
*ptr++ = txdr_unsigned(buflen);
|
||||||
reqlen += sizeof(uint32_t);
|
reqlen += sizeof(uint32_t);
|
||||||
memcpy(ptr, buffer, writesize);
|
memcpy(ptr, buffer, writesize);
|
||||||
reqlen += writesize;
|
reqlen += uint32_alignup(writesize);
|
||||||
|
|
||||||
/* Perform the write */
|
/* Perform the write */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_WRITE]++;
|
nfs_statistics(NFSPROC_WRITE);
|
||||||
error = nfs_request(nmp, NFSPROC_WRITE,
|
error = nfs_request(nmp, NFSPROC_WRITE,
|
||||||
(FAR const void *)np->n_iobuffer, reqlen,
|
(FAR const void *)np->n_iobuffer, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_write));
|
(FAR void *)&resok, sizeof(struct rpc_reply_write));
|
||||||
|
@ -1196,7 +1196,7 @@ static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
|
||||||
|
|
||||||
/* And read the directory */
|
/* And read the directory */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_READDIR]++;
|
nfs_statistics(NFSPROC_READDIR);
|
||||||
error = nfs_request(nmp, NFSPROC_READDIR,
|
error = nfs_request(nmp, NFSPROC_READDIR,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)buffer, sizeof(buffer));
|
(FAR void *)buffer, sizeof(buffer));
|
||||||
|
@ -1503,78 +1503,6 @@ void nfs_decode_args(struct nfsmount *nmp, struct nfs_args *argp)
|
||||||
nmp->nm_readdirsize = maxio;
|
nmp->nm_readdirsize = maxio;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
|
|
||||||
argp->maxgrouplist <= NFS_MAXGRPS)
|
|
||||||
{
|
|
||||||
nmp->nm_numgrps = argp->maxgrouplist;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
|
|
||||||
argp->readahead <= NFS_MAXRAHEAD)
|
|
||||||
{
|
|
||||||
nmp->nm_readahead = argp->readahead;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (argp->flags & NFSMNT_ACREGMIN && argp->acregmin >= 0)
|
|
||||||
{
|
|
||||||
if (argp->acregmin > 0xffff)
|
|
||||||
{
|
|
||||||
nmp->nm_acregmin = 0xffff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nmp->nm_acregmin = argp->acregmin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argp->flags & NFSMNT_ACREGMAX && argp->acregmax >= 0)
|
|
||||||
{
|
|
||||||
if (argp->acregmax > 0xffff)
|
|
||||||
{
|
|
||||||
nmp->nm_acregmax = 0xffff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nmp->nm_acregmax = argp->acregmax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nmp->nm_acregmin > nmp->nm_acregmax)
|
|
||||||
{
|
|
||||||
nmp->nm_acregmin = nmp->nm_acregmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argp->flags & NFSMNT_ACDIRMIN && argp->acdirmin >= 0)
|
|
||||||
{
|
|
||||||
if (argp->acdirmin > 0xffff)
|
|
||||||
{
|
|
||||||
nmp->nm_acdirmin = 0xffff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nmp->nm_acdirmin = argp->acdirmin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argp->flags & NFSMNT_ACDIRMAX && argp->acdirmax >= 0)
|
|
||||||
{
|
|
||||||
if (argp->acdirmax > 0xffff)
|
|
||||||
{
|
|
||||||
nmp->nm_acdirmax = 0xffff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nmp->nm_acdirmax = argp->acdirmax;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nmp->nm_acdirmin > nmp->nm_acdirmax)
|
|
||||||
{
|
|
||||||
nmp->nm_acdirmin = nmp->nm_acdirmax;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nmp->nm_so && adjsock)
|
if (nmp->nm_so && adjsock)
|
||||||
{
|
{
|
||||||
nfs_disconnect(nmp);
|
nfs_disconnect(nmp);
|
||||||
|
@ -1637,13 +1565,7 @@ int mountnfs(struct nfs_args *argp, void **handle)
|
||||||
nmp->nm_wsize = NFS_WSIZE;
|
nmp->nm_wsize = NFS_WSIZE;
|
||||||
nmp->nm_rsize = NFS_RSIZE;
|
nmp->nm_rsize = NFS_RSIZE;
|
||||||
nmp->nm_readdirsize = NFS_READDIRSIZE;
|
nmp->nm_readdirsize = NFS_READDIRSIZE;
|
||||||
nmp->nm_numgrps = NFS_MAXGRPS;
|
|
||||||
nmp->nm_readahead = NFS_DEFRAHEAD;
|
|
||||||
nmp->nm_fhsize = NFSX_V3FHMAX;
|
nmp->nm_fhsize = NFSX_V3FHMAX;
|
||||||
nmp->nm_acregmin = NFS_MINATTRTIMO;
|
|
||||||
nmp->nm_acregmax = NFS_MAXATTRTIMO;
|
|
||||||
nmp->nm_acdirmin = NFS_MINATTRTIMO;
|
|
||||||
nmp->nm_acdirmax = NFS_MAXATTRTIMO;
|
|
||||||
|
|
||||||
strncpy(nmp->nm_path, argp->path, 90);
|
strncpy(nmp->nm_path, argp->path, 90);
|
||||||
memcpy(&nmp->nm_nam, &argp->addr, argp->addrlen);
|
memcpy(&nmp->nm_nam, &argp->addr, argp->addrlen);
|
||||||
|
@ -1885,7 +1807,7 @@ static int nfs_statfs(struct inode *mountpt, struct statfs *sbp)
|
||||||
(void)nfs_fsinfo(nmp);
|
(void)nfs_fsinfo(nmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_FSSTAT]++;
|
nfs_statistics(NFSPROC_FSSTAT);
|
||||||
memset(&fsstat, 0, sizeof(struct FS3args));
|
memset(&fsstat, 0, sizeof(struct FS3args));
|
||||||
memset(&sfp, 0, sizeof(struct rpc_reply_fsstat));
|
memset(&sfp, 0, sizeof(struct rpc_reply_fsstat));
|
||||||
fsstat.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
|
fsstat.fsroot.length = txdr_unsigned(nmp->nm_fhsize);
|
||||||
|
@ -2005,7 +1927,7 @@ static int nfs_remove(struct inode *mountpt, const char *relpath)
|
||||||
|
|
||||||
/* Perform the REMOVE RPC call */
|
/* Perform the REMOVE RPC call */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_REMOVE]++;
|
nfs_statistics(NFSPROC_REMOVE);
|
||||||
error = nfs_request(nmp, NFSPROC_REMOVE,
|
error = nfs_request(nmp, NFSPROC_REMOVE,
|
||||||
(FAR const void *)&remove, reqlen,
|
(FAR const void *)&remove, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_remove));
|
(FAR void *)&resok, sizeof(struct rpc_reply_remove));
|
||||||
|
@ -2144,7 +2066,7 @@ static int nfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
|
||||||
|
|
||||||
/* Perform the MKDIR RPC */
|
/* Perform the MKDIR RPC */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_MKDIR]++;
|
nfs_statistics(NFSPROC_MKDIR);
|
||||||
error = nfs_request(nmp, NFSPROC_MKDIR,
|
error = nfs_request(nmp, NFSPROC_MKDIR,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_mkdir));
|
(FAR void *)&resok, sizeof(struct rpc_reply_mkdir));
|
||||||
|
@ -2235,7 +2157,7 @@ static int nfs_rmdir(struct inode *mountpt, const char *relpath)
|
||||||
|
|
||||||
/* Perform the RMDIR RPC */
|
/* Perform the RMDIR RPC */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_RMDIR]++;
|
nfs_statistics(NFSPROC_RMDIR);
|
||||||
error = nfs_request(nmp, NFSPROC_RMDIR,
|
error = nfs_request(nmp, NFSPROC_RMDIR,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_rmdir));
|
(FAR void *)&resok, sizeof(struct rpc_reply_rmdir));
|
||||||
|
@ -2367,7 +2289,7 @@ static int nfs_rename(struct inode *mountpt, const char *oldrelpath,
|
||||||
|
|
||||||
/* Perform the RENAME RPC */
|
/* Perform the RENAME RPC */
|
||||||
|
|
||||||
nfsstats.rpccnt[NFSPROC_RENAME]++;
|
nfs_statistics(NFSPROC_RENAME);
|
||||||
error = nfs_request(nmp, NFSPROC_RENAME,
|
error = nfs_request(nmp, NFSPROC_RENAME,
|
||||||
(FAR const void *)&request, reqlen,
|
(FAR const void *)&request, reqlen,
|
||||||
(FAR void *)&resok, sizeof(struct rpc_reply_rename));
|
(FAR void *)&resok, sizeof(struct rpc_reply_rename));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* fs/nfs/rpc.h
|
* fs/nfs/RPC.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2012 Gregory Nutt. All rights reserved.
|
||||||
* Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
|
* Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
#define AUTH_REJECTVERF 4
|
#define AUTH_REJECTVERF 4
|
||||||
#define AUTH_TOOWEAK 5
|
#define AUTH_TOOWEAK 5
|
||||||
|
|
||||||
/* Sizes of rpc header parts */
|
/* Sizes of RPC header parts */
|
||||||
|
|
||||||
#define RPC_SIZ 24
|
#define RPC_SIZ 24
|
||||||
#define RPC_REPLYSIZ 28
|
#define RPC_REPLYSIZ 28
|
||||||
|
@ -144,10 +144,10 @@
|
||||||
|
|
||||||
/* for rpcclnt's rc_flags */
|
/* for rpcclnt's rc_flags */
|
||||||
|
|
||||||
#define RPCCLNT_SOFT 0x001 /* soft mount (hard is details) */
|
#define RPCCLNT_SOFT (1 << 0) /* soft mount (hard is details) */
|
||||||
#define RPCCLNT_INT 0x002 /* allow interrupts on hard mounts */
|
#define RPCCLNT_INT (1 << 1) /* allow interrupts on hard mounts */
|
||||||
#define RPCCLNT_NOCONN 0x004 /* dont connect the socket (udp) */
|
#define RPCCLNT_NOCONN (1 << 2) /* dont connect the socket (udp) */
|
||||||
#define RPCCLNT_DUMBTIMR 0x010
|
#define RPCCLNT_DUMBTIMR (1 << 4)
|
||||||
|
|
||||||
/* XXX should be replaced with real locks */
|
/* XXX should be replaced with real locks */
|
||||||
|
|
||||||
|
@ -184,27 +184,20 @@
|
||||||
|
|
||||||
/* Flag values for r_flags */
|
/* Flag values for r_flags */
|
||||||
|
|
||||||
#define TASK_TIMING 0x01 /* timing request (in mntp) */
|
#define TASK_TIMING (1 << 0) /* timing request (in mntp) */
|
||||||
#define TASK_SENT 0x02 /* request has been sent */
|
#define TASK_SENT (1 << 1) /* request has been sent */
|
||||||
#define TASK_SOFTTERM 0x04 /* soft mnt, too many retries */
|
#define TASK_SOFTTERM (1 << 2) /* soft mnt, too many retries */
|
||||||
|
#define TASK_INTR (1 << 3) /* intr mnt, signal pending */
|
||||||
|
#define TASK_SOCKERR (1 << 4) /* Fatal error on socket */
|
||||||
#define TASK_INTR 0x08 /* intr mnt, signal pending */
|
#define TASK_TPRINTFMSG (1 << 5) /* Did a tprintf msg. */
|
||||||
#define TASK_SOCKERR 0x10 /* Fatal error on socket */
|
#define TASK_MUSTRESEND (1 << 6) /* Must resend request */
|
||||||
|
#define TASK_GETONEREP (1 << 7) /* Probe for one reply only */
|
||||||
|
|
||||||
#define TASK_TPRINTFMSG 0x20 /* Did a tprintf msg. */
|
|
||||||
|
|
||||||
#define TASK_MUSTRESEND 0x40 /* Must resend request */
|
|
||||||
#define TASK_GETONEREP 0x80 /* Probe for one reply only */
|
|
||||||
|
|
||||||
|
|
||||||
#define RPC_HZ (CLOCKS_PER_SEC / rpcclnt_ticks) /* Ticks/sec */
|
#define RPC_HZ (CLOCKS_PER_SEC / rpcclnt_ticks) /* Ticks/sec */
|
||||||
#define RPC_TIMEO (1 * RPC_HZ) /* Default timeout = 1 second */
|
#define RPC_TIMEO (1 * RPC_HZ) /* Default timeout = 1 second */
|
||||||
|
|
||||||
#define RPC_MAXREXMIT 100 /* Stop counting after this many */
|
#define RPC_MAXREXMIT 100 /* Stop counting after this many */
|
||||||
|
|
||||||
|
|
||||||
#define RPCIGNORE_SOERROR(s, e) \
|
#define RPCIGNORE_SOERROR(s, e) \
|
||||||
((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK)
|
((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK)
|
||||||
|
|
||||||
|
@ -298,7 +291,7 @@ struct rpc_call_header
|
||||||
{
|
{
|
||||||
uint32_t rp_xid; /* request transaction id */
|
uint32_t rp_xid; /* request transaction id */
|
||||||
int32_t rp_direction; /* call direction (0) */
|
int32_t rp_direction; /* call direction (0) */
|
||||||
uint32_t rp_rpcvers; /* rpc version (2) */
|
uint32_t rp_rpcvers; /* RPC version (2) */
|
||||||
uint32_t rp_prog; /* program */
|
uint32_t rp_prog; /* program */
|
||||||
uint32_t rp_vers; /* version */
|
uint32_t rp_vers; /* version */
|
||||||
uint32_t rp_proc; /* procedure */
|
uint32_t rp_proc; /* procedure */
|
||||||
|
@ -517,32 +510,28 @@ struct rpctask
|
||||||
dq_entry_t r_chain;
|
dq_entry_t r_chain;
|
||||||
struct rpcclnt *r_rpcclnt;
|
struct rpcclnt *r_rpcclnt;
|
||||||
uint32_t r_xid;
|
uint32_t r_xid;
|
||||||
int r_flags; /* flags on request, see below */
|
uint8_t r_flags; /* flags on request, see below */
|
||||||
int r_retry; /* max retransmission count */
|
int8_t r_retry; /* max retransmission count */
|
||||||
int r_rexmit; /* current retrans count */
|
int8_t r_rexmit; /* current retrans count */
|
||||||
int r_timer; /* tick counter on reply */
|
uint8_t r_procnum; /* NFS procedure number */
|
||||||
int r_procnum; /* NFS procedure number */
|
int8_t r_rtt; /* RTT for RPC */
|
||||||
int r_rtt; /* RTT for rpc */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rpcclnt
|
struct rpcclnt
|
||||||
{
|
{
|
||||||
int rc_flag; /* For RPCCLNT_* flags */
|
|
||||||
|
|
||||||
int rc_wsize; /* Max size of the request data */
|
|
||||||
int rc_rsize; /* Max size of the response data */
|
|
||||||
nfsfh_t rc_fh; /* File handle of root dir */
|
nfsfh_t rc_fh; /* File handle of root dir */
|
||||||
char *rc_path; /* server's path of the directory being mount */
|
char *rc_path; /* server's path of the directory being mount */
|
||||||
|
|
||||||
struct sockaddr *rc_name;
|
struct sockaddr *rc_name;
|
||||||
struct socket *rc_so; /* Rpc socket */
|
struct socket *rc_so; /* RPC socket */
|
||||||
|
|
||||||
|
uint8_t rc_flag; /* For RPCCLNT_* flags */
|
||||||
uint8_t rc_sotype; /* Type of socket */
|
uint8_t rc_sotype; /* Type of socket */
|
||||||
int rc_soproto; /* and protocol */
|
uint8_t rc_soproto; /* and protocol */
|
||||||
uint8_t rc_soflags; /* pr_flags for socket protocol */
|
uint8_t rc_soflags; /* pr_flags for socket protocol */
|
||||||
|
uint8_t rc_retry; /* Max retries */
|
||||||
|
uint8_t rc_timeo; /* Init timer for NFSMNT_DUMBTIMR */
|
||||||
|
|
||||||
int rc_timeo; /* Init timer for NFSMNT_DUMBTIMR */
|
|
||||||
int rc_retry; /* Max retries */
|
|
||||||
int rc_srtt[4]; /* Timers for rpcs */
|
int rc_srtt[4]; /* Timers for rpcs */
|
||||||
int rc_sdrtt[4];
|
int rc_sdrtt[4];
|
||||||
int rc_sent; /* Request send count */
|
int rc_sent; /* Request send count */
|
||||||
|
@ -572,12 +561,12 @@ struct rpcclnt
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void rpcclnt_init(void);
|
void rpcclnt_init(void);
|
||||||
int rpcclnt_connect(struct rpcclnt *rpc);
|
int rpcclnt_connect(struct rpcclnt *RPC);
|
||||||
int rpcclnt_reconnect(struct rpctask *rep);
|
int rpcclnt_reconnect(struct rpctask *rep);
|
||||||
void rpcclnt_disconnect(struct rpcclnt *rpc);
|
void rpcclnt_disconnect(struct rpcclnt *RPC);
|
||||||
int rpcclnt_umount(struct rpcclnt *rpc);
|
int rpcclnt_umount(struct rpcclnt *RPC);
|
||||||
void rpcclnt_safedisconnect(struct rpcclnt *rpc);
|
void rpcclnt_safedisconnect(struct rpcclnt *RPC);
|
||||||
int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog, int version,
|
int rpcclnt_request(FAR struct rpcclnt *RPC, int procnum, int prog, int version,
|
||||||
FAR const void *request, size_t reqlen,
|
FAR const void *request, size_t reqlen,
|
||||||
FAR void *response, size_t resplen);
|
FAR void *response, size_t resplen);
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,6 @@
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Pre-processor Definitions
|
* Pre-processor Definitions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/* Estimate rto for an nfs rpc sent via. an unreliable datagram. Use the mean
|
/* Estimate rto for an nfs rpc sent via. an unreliable datagram. Use the mean
|
||||||
* and mean deviation of rtt for the appropriate type of rpc for the frequent
|
* and mean deviation of rtt for the appropriate type of rpc for the frequent
|
||||||
* rpcs and a default for the others. The justification for doing "other"
|
* rpcs and a default for the others. The justification for doing "other"
|
||||||
|
@ -711,12 +710,9 @@ static int rpcclnt_reply(struct rpctask *myrep, int procid, int prog,
|
||||||
|
|
||||||
if (rep->r_flags & TASK_TIMING)
|
if (rep->r_flags & TASK_TIMING)
|
||||||
{
|
{
|
||||||
/* Since the timer resolution of
|
/* Since the timer resolution of is so coarse, it can often
|
||||||
* NFS_HZ is so course, it can often
|
* result in r_rtt == 0. Since r_rtt == N means that the actual
|
||||||
* result in r_rtt == 0. Since r_rtt
|
* rtt is between N+dt and N+2-dt ticks, add 1.
|
||||||
* == N means that the actual rtt is
|
|
||||||
* between N+dt and N+2-dt ticks, add
|
|
||||||
* 1.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
t1 = rep->r_rtt + 1;
|
t1 = rep->r_rtt + 1;
|
||||||
|
@ -1824,7 +1820,7 @@ int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
|
||||||
* update the call messsage size.
|
* update the call messsage size.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
callmsg = request;
|
callmsg = (FAR void *)request;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue