mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
nfsd: add data structures for handling CB_NOTIFY
Add the data structures, allocation helpers, and callback operations needed for directory delegation CB_NOTIFY support: - struct nfsd_notify_event: carries fsnotify events for CB_NOTIFY - struct nfsd4_cb_notify: per-delegation state for notification handling - Union dl_cb_fattr with dl_cb_notify in nfs4_delegation since a delegation is either a regular file delegation or a directory delegation, never both Refactor alloc_init_deleg() into a common __alloc_init_deleg() base with a pluggable sc_free callback, and add alloc_init_dir_deleg() which allocates the page array and notify4 buffer needed for CB_NOTIFY encoding. Add skeleton nfsd4_cb_notify_ops with done/release handlers that will be filled in when the notification path is wired up. Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260616-dir-deleg-v7-9-6cbc7eac0ade@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
This commit is contained in:
parent
a66d695c95
commit
b79feed6f0
|
|
@ -126,6 +126,7 @@ static void free_session(struct nfsd4_session *);
|
||||||
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
|
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
|
||||||
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
|
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
|
||||||
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops;
|
static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops;
|
||||||
|
static const struct nfsd4_callback_ops nfsd4_cb_notify_ops;
|
||||||
|
|
||||||
static struct workqueue_struct *laundry_wq;
|
static struct workqueue_struct *laundry_wq;
|
||||||
|
|
||||||
|
|
@ -1123,29 +1124,31 @@ static void block_delegations(struct knfsd_fh *fh)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct nfs4_delegation *
|
static struct nfs4_delegation *
|
||||||
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
__alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||||
struct nfs4_clnt_odstate *odstate, u32 dl_type)
|
struct nfs4_clnt_odstate *odstate, u32 dl_type,
|
||||||
|
void (*sc_free)(struct nfs4_stid *))
|
||||||
{
|
{
|
||||||
struct nfs4_delegation *dp;
|
struct nfs4_delegation *dp;
|
||||||
struct nfs4_stid *stid;
|
struct nfs4_stid *stid;
|
||||||
long n;
|
long n;
|
||||||
|
|
||||||
dprintk("NFSD alloc_init_deleg\n");
|
if (delegation_blocked(&fp->fi_fhandle))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
n = atomic_long_inc_return(&num_delegations);
|
n = atomic_long_inc_return(&num_delegations);
|
||||||
if (n < 0 || n > max_delegations)
|
if (n < 0 || n > max_delegations)
|
||||||
goto out_dec;
|
goto out_dec;
|
||||||
if (delegation_blocked(&fp->fi_fhandle))
|
|
||||||
goto out_dec;
|
stid = nfs4_alloc_stid(clp, deleg_slab, sc_free);
|
||||||
stid = nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg);
|
|
||||||
if (stid == NULL)
|
if (stid == NULL)
|
||||||
goto out_dec;
|
goto out_dec;
|
||||||
dp = delegstateid(stid);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* delegation seqid's are never incremented. The 4.1 special
|
* delegation seqid's are never incremented. The 4.1 special
|
||||||
* meaning of seqid 0 isn't meaningful, really, but let's avoid
|
* meaning of seqid 0 isn't meaningful, really, but let's avoid
|
||||||
* 0 anyway just for consistency and use 1:
|
* 0 anyway just for consistency and use 1.
|
||||||
*/
|
*/
|
||||||
|
dp = delegstateid(stid);
|
||||||
dp->dl_stid.sc_stateid.si_generation = 1;
|
dp->dl_stid.sc_stateid.si_generation = 1;
|
||||||
INIT_LIST_HEAD(&dp->dl_perfile);
|
INIT_LIST_HEAD(&dp->dl_perfile);
|
||||||
INIT_LIST_HEAD(&dp->dl_perclnt);
|
INIT_LIST_HEAD(&dp->dl_perclnt);
|
||||||
|
|
@ -1155,19 +1158,79 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||||
dp->dl_type = dl_type;
|
dp->dl_type = dl_type;
|
||||||
dp->dl_retries = 1;
|
dp->dl_retries = 1;
|
||||||
dp->dl_recalled = false;
|
dp->dl_recalled = false;
|
||||||
nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
|
|
||||||
&nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
|
|
||||||
nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
|
|
||||||
&nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
|
|
||||||
dp->dl_cb_fattr.ncf_file_modified = false;
|
|
||||||
get_nfs4_file(fp);
|
get_nfs4_file(fp);
|
||||||
dp->dl_stid.sc_file = fp;
|
dp->dl_stid.sc_file = fp;
|
||||||
|
nfsd4_init_cb(&dp->dl_recall, dp->dl_stid.sc_client,
|
||||||
|
&nfsd4_cb_recall_ops, NFSPROC4_CLNT_CB_RECALL);
|
||||||
return dp;
|
return dp;
|
||||||
out_dec:
|
out_dec:
|
||||||
atomic_long_dec(&num_delegations);
|
atomic_long_dec(&num_delegations);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct nfs4_delegation *
|
||||||
|
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||||
|
struct nfs4_clnt_odstate *odstate, u32 dl_type)
|
||||||
|
{
|
||||||
|
struct nfs4_delegation *dp;
|
||||||
|
|
||||||
|
dp = __alloc_init_deleg(clp, fp, odstate, dl_type, nfs4_free_deleg);
|
||||||
|
if (!dp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
|
||||||
|
&nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
|
||||||
|
dp->dl_cb_fattr.ncf_file_modified = false;
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nfs4_free_dir_deleg(struct nfs4_stid *stid)
|
||||||
|
{
|
||||||
|
struct nfs4_delegation *dp = delegstateid(stid);
|
||||||
|
struct nfsd4_cb_notify *ncn = &dp->dl_cb_notify;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ncn->ncn_evt_cnt; ++i)
|
||||||
|
nfsd_notify_event_put(ncn->ncn_evt[i]);
|
||||||
|
kfree(ncn->ncn_nf);
|
||||||
|
for (i = 0; i < NOTIFY4_PAGE_ARRAY_SIZE; i++) {
|
||||||
|
if (!ncn->ncn_pages[i])
|
||||||
|
break;
|
||||||
|
put_page(ncn->ncn_pages[i]);
|
||||||
|
}
|
||||||
|
nfs4_free_deleg(stid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nfs4_delegation *
|
||||||
|
alloc_init_dir_deleg(struct nfs4_client *clp, struct nfs4_file *fp)
|
||||||
|
{
|
||||||
|
struct nfs4_delegation *dp;
|
||||||
|
struct nfsd4_cb_notify *ncn;
|
||||||
|
int npages;
|
||||||
|
|
||||||
|
dp = __alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ, nfs4_free_dir_deleg);
|
||||||
|
if (!dp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ncn = &dp->dl_cb_notify;
|
||||||
|
|
||||||
|
npages = alloc_pages_bulk(GFP_KERNEL, NOTIFY4_PAGE_ARRAY_SIZE, ncn->ncn_pages);
|
||||||
|
if (npages != NOTIFY4_PAGE_ARRAY_SIZE) {
|
||||||
|
nfs4_put_stid(&dp->dl_stid);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncn->ncn_nf = kcalloc(NOTIFY4_EVENT_QUEUE_SIZE, sizeof(*ncn->ncn_nf), GFP_KERNEL);
|
||||||
|
if (!ncn->ncn_nf) {
|
||||||
|
nfs4_put_stid(&dp->dl_stid);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
spin_lock_init(&ncn->ncn_lock);
|
||||||
|
nfsd4_init_cb(&ncn->ncn_cb, dp->dl_stid.sc_client,
|
||||||
|
&nfsd4_cb_notify_ops, NFSPROC4_CLNT_CB_NOTIFY);
|
||||||
|
return dp;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nfs4_put_stid(struct nfs4_stid *s)
|
nfs4_put_stid(struct nfs4_stid *s)
|
||||||
{
|
{
|
||||||
|
|
@ -3422,6 +3485,30 @@ nfsd4_cb_getattr_release(struct nfsd4_callback *cb)
|
||||||
nfs4_put_stid(&dp->dl_stid);
|
nfs4_put_stid(&dp->dl_stid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfsd4_cb_notify_done(struct nfsd4_callback *cb,
|
||||||
|
struct rpc_task *task)
|
||||||
|
{
|
||||||
|
switch (task->tk_status) {
|
||||||
|
case -NFS4ERR_DELAY:
|
||||||
|
rpc_delay(task, 2 * HZ);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nfsd4_cb_notify_release(struct nfsd4_callback *cb)
|
||||||
|
{
|
||||||
|
struct nfsd4_cb_notify *ncn =
|
||||||
|
container_of(cb, struct nfsd4_cb_notify, ncn_cb);
|
||||||
|
struct nfs4_delegation *dp =
|
||||||
|
container_of(ncn, struct nfs4_delegation, dl_cb_notify);
|
||||||
|
|
||||||
|
nfs4_put_stid(&dp->dl_stid);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
|
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
|
||||||
.done = nfsd4_cb_recall_any_done,
|
.done = nfsd4_cb_recall_any_done,
|
||||||
.release = nfsd4_cb_recall_any_release,
|
.release = nfsd4_cb_recall_any_release,
|
||||||
|
|
@ -3434,6 +3521,12 @@ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
|
||||||
.opcode = OP_CB_GETATTR,
|
.opcode = OP_CB_GETATTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct nfsd4_callback_ops nfsd4_cb_notify_ops = {
|
||||||
|
.done = nfsd4_cb_notify_done,
|
||||||
|
.release = nfsd4_cb_notify_release,
|
||||||
|
.opcode = OP_CB_NOTIFY,
|
||||||
|
};
|
||||||
|
|
||||||
static void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
|
static void nfs4_cb_getattr(struct nfs4_cb_fattr *ncf)
|
||||||
{
|
{
|
||||||
struct nfs4_delegation *dp =
|
struct nfs4_delegation *dp =
|
||||||
|
|
@ -9810,7 +9903,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||||
|
|
||||||
/* Try to set up the lease */
|
/* Try to set up the lease */
|
||||||
status = -ENOMEM;
|
status = -ENOMEM;
|
||||||
dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
|
dp = alloc_init_dir_deleg(clp, fp);
|
||||||
if (!dp)
|
if (!dp)
|
||||||
goto out_delegees;
|
goto out_delegees;
|
||||||
if (cstate->current_fh.fh_export)
|
if (cstate->current_fh.fh_export)
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,45 @@ struct nfs4_cb_fattr {
|
||||||
#define NOTIFY4_EVENT_QUEUE_SIZE 3
|
#define NOTIFY4_EVENT_QUEUE_SIZE 3
|
||||||
#define NOTIFY4_PAGE_ARRAY_SIZE 1
|
#define NOTIFY4_PAGE_ARRAY_SIZE 1
|
||||||
|
|
||||||
|
struct nfsd_notify_event {
|
||||||
|
refcount_t ne_ref; // refcount
|
||||||
|
u32 ne_mask; // FS_* mask from fsnotify callback
|
||||||
|
struct dentry *ne_dentry; // dentry reference to target
|
||||||
|
u32 ne_namelen; // length of ne_name
|
||||||
|
char ne_name[]; // name of dentry being changed
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct nfsd_notify_event *nfsd_notify_event_get(struct nfsd_notify_event *ne)
|
||||||
|
{
|
||||||
|
refcount_inc(&ne->ne_ref);
|
||||||
|
return ne;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nfsd_notify_event_put(struct nfsd_notify_event *ne)
|
||||||
|
{
|
||||||
|
if (refcount_dec_and_test(&ne->ne_ref)) {
|
||||||
|
dput(ne->ne_dentry);
|
||||||
|
kfree(ne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a directory delegation. The callback is for handling CB_NOTIFYs.
|
||||||
|
* As notifications from fsnotify come in, allocate a new event, take the ncn_lock,
|
||||||
|
* and add it to the ncn_evt queue. The CB_NOTIFY prepare handler will take the
|
||||||
|
* lock, clean out the list and process it.
|
||||||
|
*/
|
||||||
|
struct nfsd4_cb_notify {
|
||||||
|
spinlock_t ncn_lock; // protects the evt queue and count
|
||||||
|
int ncn_evt_cnt; // count of events in ncn_evt
|
||||||
|
int ncn_nf_cnt; // count of valid entries in ncn_nf
|
||||||
|
struct nfsd_notify_event *ncn_evt[NOTIFY4_EVENT_QUEUE_SIZE]; // list of events
|
||||||
|
struct page *ncn_pages[NOTIFY4_PAGE_ARRAY_SIZE]; // for encoding
|
||||||
|
struct notify4 *ncn_nf; // array of notify4's to be sent
|
||||||
|
bool ncn_encode_err; // did encoding fail?
|
||||||
|
struct nfsd4_callback ncn_cb; // notify4 callback
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Represents a delegation stateid. The nfs4_client holds references to these
|
* Represents a delegation stateid. The nfs4_client holds references to these
|
||||||
* and they are put when it is being destroyed or when the delegation is
|
* and they are put when it is being destroyed or when the delegation is
|
||||||
|
|
@ -233,8 +272,12 @@ struct nfs4_delegation {
|
||||||
bool dl_written;
|
bool dl_written;
|
||||||
bool dl_setattr;
|
bool dl_setattr;
|
||||||
|
|
||||||
/* for CB_GETATTR */
|
union {
|
||||||
struct nfs4_cb_fattr dl_cb_fattr;
|
/* for CB_GETATTR */
|
||||||
|
struct nfs4_cb_fattr dl_cb_fattr;
|
||||||
|
/* for CB_NOTIFY */
|
||||||
|
struct nfsd4_cb_notify dl_cb_notify;
|
||||||
|
};
|
||||||
|
|
||||||
/* For delegated timestamps */
|
/* For delegated timestamps */
|
||||||
struct timespec64 dl_atime;
|
struct timespec64 dl_atime;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user