mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +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:
+107
-14
@@ -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_notify_lock_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;
|
||||
|
||||
@@ -1123,29 +1124,31 @@ static void block_delegations(struct knfsd_fh *fh)
|
||||
}
|
||||
|
||||
static struct nfs4_delegation *
|
||||
alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||
struct nfs4_clnt_odstate *odstate, u32 dl_type)
|
||||
__alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||
struct nfs4_clnt_odstate *odstate, u32 dl_type,
|
||||
void (*sc_free)(struct nfs4_stid *))
|
||||
{
|
||||
struct nfs4_delegation *dp;
|
||||
struct nfs4_stid *stid;
|
||||
long n;
|
||||
|
||||
dprintk("NFSD alloc_init_deleg\n");
|
||||
if (delegation_blocked(&fp->fi_fhandle))
|
||||
return NULL;
|
||||
|
||||
n = atomic_long_inc_return(&num_delegations);
|
||||
if (n < 0 || n > max_delegations)
|
||||
goto out_dec;
|
||||
if (delegation_blocked(&fp->fi_fhandle))
|
||||
goto out_dec;
|
||||
stid = nfs4_alloc_stid(clp, deleg_slab, nfs4_free_deleg);
|
||||
|
||||
stid = nfs4_alloc_stid(clp, deleg_slab, sc_free);
|
||||
if (stid == NULL)
|
||||
goto out_dec;
|
||||
dp = delegstateid(stid);
|
||||
|
||||
/*
|
||||
* delegation seqid's are never incremented. The 4.1 special
|
||||
* 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;
|
||||
INIT_LIST_HEAD(&dp->dl_perfile);
|
||||
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_retries = 1;
|
||||
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);
|
||||
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;
|
||||
out_dec:
|
||||
atomic_long_dec(&num_delegations);
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.done = nfsd4_cb_recall_any_done,
|
||||
.release = nfsd4_cb_recall_any_release,
|
||||
@@ -3434,6 +3521,12 @@ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
|
||||
.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)
|
||||
{
|
||||
struct nfs4_delegation *dp =
|
||||
@@ -9810,7 +9903,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||
|
||||
/* Try to set up the lease */
|
||||
status = -ENOMEM;
|
||||
dp = alloc_init_deleg(clp, fp, NULL, NFS4_OPEN_DELEGATE_READ);
|
||||
dp = alloc_init_dir_deleg(clp, fp);
|
||||
if (!dp)
|
||||
goto out_delegees;
|
||||
if (cstate->current_fh.fh_export)
|
||||
|
||||
+45
-2
@@ -197,6 +197,45 @@ struct nfs4_cb_fattr {
|
||||
#define NOTIFY4_EVENT_QUEUE_SIZE 3
|
||||
#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
|
||||
* 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_setattr;
|
||||
|
||||
/* for CB_GETATTR */
|
||||
struct nfs4_cb_fattr dl_cb_fattr;
|
||||
union {
|
||||
/* for CB_GETATTR */
|
||||
struct nfs4_cb_fattr dl_cb_fattr;
|
||||
/* for CB_NOTIFY */
|
||||
struct nfsd4_cb_notify dl_cb_notify;
|
||||
};
|
||||
|
||||
/* For delegated timestamps */
|
||||
struct timespec64 dl_atime;
|
||||
|
||||
Reference in New Issue
Block a user