提交 af558e33 编写于 作者: J J. Bruce Fields

nfsd: common grace period control

Rewrite grace period code to unify management of grace period across
lockd and nfsd.  The current code has lockd and nfsd cooperate to
compute a grace period which is satisfactory to them both, and then
individually enforce it.  This creates a slight race condition, since
the enforcement is not coordinated.  It's also more complicated than
necessary.

Here instead we have lockd and nfsd each inform common code when they
enter the grace period, and when they're ready to leave the grace
period, and allow normal locking only after both of them are ready to
leave.

We also expect the locks_start_grace()/locks_end_grace() interface here
to be simpler to build on for future cluster/high-availability work,
which may require (for example) putting individual filesystems into
grace, or enforcing grace periods across multiple cluster nodes.
Signed-off-by: NJ. Bruce Fields <bfields@citi.umich.edu>
上级 d5b337b4
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
obj-$(CONFIG_LOCKD) += lockd.o obj-$(CONFIG_LOCKD) += lockd.o
lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
svcproc.o svcsubs.o mon.o xdr.o svcproc.o svcsubs.o mon.o xdr.o grace.o
lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
lockd-objs := $(lockd-objs-y) lockd-objs := $(lockd-objs-y)
/*
* Common code for control of lockd and nfsv4 grace periods.
*/
#include <linux/module.h>
#include <linux/lockd/bind.h>
static LIST_HEAD(grace_list);
static DEFINE_SPINLOCK(grace_lock);
/**
* locks_start_grace
* @lm: who this grace period is for
*
* A grace period is a period during which locks should not be given
* out. Currently grace periods are only enforced by the two lock
* managers (lockd and nfsd), using the locks_in_grace() function to
* check when they are in a grace period.
*
* This function is called to start a grace period.
*/
void locks_start_grace(struct lock_manager *lm)
{
spin_lock(&grace_lock);
list_add(&lm->list, &grace_list);
spin_unlock(&grace_lock);
}
EXPORT_SYMBOL_GPL(locks_start_grace);
/**
* locks_end_grace
* @lm: who this grace period is for
*
* Call this function to state that the given lock manager is ready to
* resume regular locking. The grace period will not end until all lock
* managers that called locks_start_grace() also call locks_end_grace().
* Note that callers count on it being safe to call this more than once,
* and the second call should be a no-op.
*/
void locks_end_grace(struct lock_manager *lm)
{
spin_lock(&grace_lock);
list_del_init(&lm->list);
spin_unlock(&grace_lock);
}
EXPORT_SYMBOL_GPL(locks_end_grace);
/**
* locks_in_grace
*
* Lock managers call this function to determine when it is OK for them
* to answer ordinary lock requests, and when they should accept only
* lock reclaims.
*/
int locks_in_grace(void)
{
return !list_empty(&grace_list);
}
EXPORT_SYMBOL_GPL(locks_in_grace);
...@@ -51,7 +51,6 @@ static DEFINE_MUTEX(nlmsvc_mutex); ...@@ -51,7 +51,6 @@ static DEFINE_MUTEX(nlmsvc_mutex);
static unsigned int nlmsvc_users; static unsigned int nlmsvc_users;
static struct task_struct *nlmsvc_task; static struct task_struct *nlmsvc_task;
static struct svc_rqst *nlmsvc_rqst; static struct svc_rqst *nlmsvc_rqst;
int nlmsvc_grace_period;
unsigned long nlmsvc_timeout; unsigned long nlmsvc_timeout;
/* /*
...@@ -85,30 +84,21 @@ static unsigned long get_lockd_grace_period(void) ...@@ -85,30 +84,21 @@ static unsigned long get_lockd_grace_period(void)
return nlm_timeout * 5 * HZ; return nlm_timeout * 5 * HZ;
} }
unsigned long get_nfs_grace_period(void) static struct lock_manager lockd_manager = {
{ };
unsigned long lockdgrace = get_lockd_grace_period();
unsigned long nfsdgrace = 0;
if (nlmsvc_ops)
nfsdgrace = nlmsvc_ops->get_grace_period();
return max(lockdgrace, nfsdgrace);
}
EXPORT_SYMBOL(get_nfs_grace_period);
static void grace_ender(struct work_struct *not_used) static void grace_ender(struct work_struct *not_used)
{ {
nlmsvc_grace_period = 0; locks_end_grace(&lockd_manager);
} }
static DECLARE_DELAYED_WORK(grace_period_end, grace_ender); static DECLARE_DELAYED_WORK(grace_period_end, grace_ender);
static void set_grace_period(void) static void set_grace_period(void)
{ {
unsigned long grace_period = get_nfs_grace_period() + jiffies; unsigned long grace_period = get_lockd_grace_period();
nlmsvc_grace_period = 1; locks_start_grace(&lockd_manager);
cancel_delayed_work_sync(&grace_period_end); cancel_delayed_work_sync(&grace_period_end);
schedule_delayed_work(&grace_period_end, grace_period); schedule_delayed_work(&grace_period_end, grace_period);
} }
......
...@@ -89,7 +89,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -89,7 +89,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept test requests during grace period */ /* Don't accept test requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rc; return rc;
} }
...@@ -123,7 +123,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -123,7 +123,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period && !argp->reclaim) { if (locks_in_grace() && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rc; return rc;
} }
...@@ -169,7 +169,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -169,7 +169,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept requests during grace period */ /* Don't accept requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -202,7 +202,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -202,7 +202,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -341,7 +341,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -341,7 +341,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period && !argp->reclaim) { if (locks_in_grace() && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -374,7 +374,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -374,7 +374,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept requests during grace period */ /* Don't accept requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
......
...@@ -118,7 +118,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -118,7 +118,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept test requests during grace period */ /* Don't accept test requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rc; return rc;
} }
...@@ -153,7 +153,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -153,7 +153,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period && !argp->reclaim) { if (locks_in_grace() && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rc; return rc;
} }
...@@ -199,7 +199,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -199,7 +199,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept requests during grace period */ /* Don't accept requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -232,7 +232,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -232,7 +232,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -373,7 +373,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -373,7 +373,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept new lock requests during grace period */ /* Don't accept new lock requests during grace period */
if (nlmsvc_grace_period && !argp->reclaim) { if (locks_in_grace() && !argp->reclaim) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
...@@ -406,7 +406,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, ...@@ -406,7 +406,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
resp->cookie = argp->cookie; resp->cookie = argp->cookie;
/* Don't accept requests during grace period */ /* Don't accept requests during grace period */
if (nlmsvc_grace_period) { if (locks_in_grace()) {
resp->status = nlm_lck_denied_grace_period; resp->status = nlm_lck_denied_grace_period;
return rpc_success; return rpc_success;
} }
......
...@@ -70,7 +70,6 @@ nlm_fclose(struct file *filp) ...@@ -70,7 +70,6 @@ nlm_fclose(struct file *filp)
static struct nlmsvc_binding nfsd_nlm_ops = { static struct nlmsvc_binding nfsd_nlm_ops = {
.fopen = nlm_fopen, /* open file for locking */ .fopen = nlm_fopen, /* open file for locking */
.fclose = nlm_fclose, /* close file */ .fclose = nlm_fclose, /* close file */
.get_grace_period = get_nfs4_grace_period,
}; };
void void
......
...@@ -201,10 +201,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -201,10 +201,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
/* Openowner is now set, so sequence id will get bumped. Now we need /* Openowner is now set, so sequence id will get bumped. Now we need
* these checks before we do any creates: */ * these checks before we do any creates: */
status = nfserr_grace; status = nfserr_grace;
if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
goto out; goto out;
status = nfserr_no_grace; status = nfserr_no_grace;
if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
goto out; goto out;
switch (open->op_claim_type) { switch (open->op_claim_type) {
...@@ -575,7 +575,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -575,7 +575,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{ {
__be32 status; __be32 status;
if (nfs4_in_grace()) if (locks_in_grace())
return nfserr_grace; return nfserr_grace;
status = nfsd_unlink(rqstp, &cstate->current_fh, 0, status = nfsd_unlink(rqstp, &cstate->current_fh, 0,
remove->rm_name, remove->rm_namelen); remove->rm_name, remove->rm_namelen);
...@@ -596,7 +596,7 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -596,7 +596,7 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!cstate->save_fh.fh_dentry) if (!cstate->save_fh.fh_dentry)
return status; return status;
if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags
& NFSEXP_NOSUBTREECHECK)) & NFSEXP_NOSUBTREECHECK))
return nfserr_grace; return nfserr_grace;
status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname, status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname,
......
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
static time_t lease_time = 90; /* default lease time */ static time_t lease_time = 90; /* default lease time */
static time_t user_lease_time = 90; static time_t user_lease_time = 90;
static time_t boot_time; static time_t boot_time;
static int in_grace = 1;
static u32 current_ownerid = 1; static u32 current_ownerid = 1;
static u32 current_fileid = 1; static u32 current_fileid = 1;
static u32 current_delegid = 1; static u32 current_delegid = 1;
...@@ -1640,7 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta ...@@ -1640,7 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
case NFS4_OPEN_CLAIM_NULL: case NFS4_OPEN_CLAIM_NULL:
/* Let's not give out any delegations till everyone's /* Let's not give out any delegations till everyone's
* had the chance to reclaim theirs.... */ * had the chance to reclaim theirs.... */
if (nfs4_in_grace()) if (locks_in_grace())
goto out; goto out;
if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) if (!atomic_read(&cb->cb_set) || !sop->so_confirmed)
goto out; goto out;
...@@ -1816,12 +1815,15 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -1816,12 +1815,15 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return status; return status;
} }
struct lock_manager nfsd4_manager = {
};
static void static void
end_grace(void) nfsd4_end_grace(void)
{ {
dprintk("NFSD: end of grace period\n"); dprintk("NFSD: end of grace period\n");
nfsd4_recdir_purge_old(); nfsd4_recdir_purge_old();
in_grace = 0; locks_end_grace(&nfsd4_manager);
} }
static time_t static time_t
...@@ -1838,8 +1840,8 @@ nfs4_laundromat(void) ...@@ -1838,8 +1840,8 @@ nfs4_laundromat(void)
nfs4_lock_state(); nfs4_lock_state();
dprintk("NFSD: laundromat service - starting\n"); dprintk("NFSD: laundromat service - starting\n");
if (in_grace) if (locks_in_grace())
end_grace(); nfsd4_end_grace();
list_for_each_safe(pos, next, &client_lru) { list_for_each_safe(pos, next, &client_lru) {
clp = list_entry(pos, struct nfs4_client, cl_lru); clp = list_entry(pos, struct nfs4_client, cl_lru);
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
...@@ -1974,7 +1976,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) ...@@ -1974,7 +1976,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
return nfserr_bad_stateid; return nfserr_bad_stateid;
else if (ONE_STATEID(stateid) && (flags & RD_STATE)) else if (ONE_STATEID(stateid) && (flags & RD_STATE))
return nfs_ok; return nfs_ok;
else if (nfs4_in_grace()) { else if (locks_in_grace()) {
/* Answer in remaining cases depends on existance of /* Answer in remaining cases depends on existance of
* conflicting state; so we must wait out the grace period. */ * conflicting state; so we must wait out the grace period. */
return nfserr_grace; return nfserr_grace;
...@@ -1993,7 +1995,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) ...@@ -1993,7 +1995,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags)
static inline int static inline int
io_during_grace_disallowed(struct inode *inode, int flags) io_during_grace_disallowed(struct inode *inode, int flags)
{ {
return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE)) return locks_in_grace() && (flags & (RD_STATE | WR_STATE))
&& mandatory_lock(inode); && mandatory_lock(inode);
} }
...@@ -2693,10 +2695,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2693,10 +2695,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
filp = lock_stp->st_vfs_file; filp = lock_stp->st_vfs_file;
status = nfserr_grace; status = nfserr_grace;
if (nfs4_in_grace() && !lock->lk_reclaim) if (locks_in_grace() && !lock->lk_reclaim)
goto out; goto out;
status = nfserr_no_grace; status = nfserr_no_grace;
if (!nfs4_in_grace() && lock->lk_reclaim) if (!locks_in_grace() && lock->lk_reclaim)
goto out; goto out;
locks_init_lock(&file_lock); locks_init_lock(&file_lock);
...@@ -2779,7 +2781,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2779,7 +2781,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
int error; int error;
__be32 status; __be32 status;
if (nfs4_in_grace()) if (locks_in_grace())
return nfserr_grace; return nfserr_grace;
if (check_lock_length(lockt->lt_offset, lockt->lt_length)) if (check_lock_length(lockt->lt_offset, lockt->lt_length))
...@@ -3192,9 +3194,9 @@ __nfs4_state_start(void) ...@@ -3192,9 +3194,9 @@ __nfs4_state_start(void)
unsigned long grace_time; unsigned long grace_time;
boot_time = get_seconds(); boot_time = get_seconds();
grace_time = get_nfs_grace_period(); grace_time = get_nfs4_grace_period();
lease_time = user_lease_time; lease_time = user_lease_time;
in_grace = 1; locks_start_grace(&nfsd4_manager);
printk(KERN_INFO "NFSD: starting %ld-second grace period\n", printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
grace_time/HZ); grace_time/HZ);
laundry_wq = create_singlethread_workqueue("nfsd4"); laundry_wq = create_singlethread_workqueue("nfsd4");
...@@ -3213,12 +3215,6 @@ nfs4_state_start(void) ...@@ -3213,12 +3215,6 @@ nfs4_state_start(void)
return; return;
} }
int
nfs4_in_grace(void)
{
return in_grace;
}
time_t time_t
nfs4_lease_time(void) nfs4_lease_time(void)
{ {
......
...@@ -942,6 +942,14 @@ struct lock_manager_operations { ...@@ -942,6 +942,14 @@ struct lock_manager_operations {
int (*fl_change)(struct file_lock **, int); int (*fl_change)(struct file_lock **, int);
}; };
struct lock_manager {
struct list_head list;
};
void locks_start_grace(struct lock_manager *);
void locks_end_grace(struct lock_manager *);
int locks_in_grace(void);
/* that will die - we need it for nfs_lock_info */ /* that will die - we need it for nfs_lock_info */
#include <linux/nfs_fs_i.h> #include <linux/nfs_fs_i.h>
......
...@@ -27,7 +27,6 @@ struct nlmsvc_binding { ...@@ -27,7 +27,6 @@ struct nlmsvc_binding {
struct nfs_fh *, struct nfs_fh *,
struct file **); struct file **);
void (*fclose)(struct file *); void (*fclose)(struct file *);
unsigned long (*get_grace_period)(void);
}; };
extern struct nlmsvc_binding * nlmsvc_ops; extern struct nlmsvc_binding * nlmsvc_ops;
...@@ -56,12 +55,4 @@ extern int nlmclnt_proc(struct nlm_host *host, int cmd, ...@@ -56,12 +55,4 @@ extern int nlmclnt_proc(struct nlm_host *host, int cmd,
extern int lockd_up(int proto); extern int lockd_up(int proto);
extern void lockd_down(void); extern void lockd_down(void);
unsigned long get_nfs_grace_period(void);
#ifdef CONFIG_NFSD_V4
unsigned long get_nfs4_grace_period(void);
#else
static inline unsigned long get_nfs4_grace_period(void) {return 0;}
#endif
#endif /* LINUX_LOCKD_BIND_H */ #endif /* LINUX_LOCKD_BIND_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册