@@ -139,6 +139,7 @@ extern struct sockaddr pnfsd_lexp_addr;
extern size_t pnfs_lexp_addr_len;
extern void pnfsd_lexp_init(struct inode *);
+extern int pnfsd_lexp_recall_layout(struct inode *);
#endif /* CONFIG_PNFSD_LOCAL_EXPORT */
#endif /* LINUX_NFSD_PNFSD_H */
@@ -18,6 +18,8 @@
* by David M. Richter <richterd@citi.umich.edu>
*/
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/nfsd/nfs4layoutxdr.h>
@@ -28,6 +30,8 @@
struct sockaddr pnfsd_lexp_addr;
size_t pnfs_lexp_addr_len;
+static wait_queue_head_t lo_recall_wq;
+
static int
pnfsd_lexp_layout_type(struct super_block *sb)
{
@@ -196,8 +200,7 @@ static int
pnfsd_lexp_layout_return(struct inode *inode,
const struct nfsd4_pnfs_layoutreturn_arg *args)
{
- dprintk("%s: (unimplemented)\n", __func__);
-
+ wake_up_all(&lo_recall_wq);
return 0;
}
@@ -220,6 +223,75 @@ static struct pnfs_export_operations pnfsd_lexp_ops = {
void
pnfsd_lexp_init(struct inode *inode)
{
+ static bool init_once;
+
dprintk("%s: &pnfsd_lexp_ops=%p\n", __func__, &pnfsd_lexp_ops);
inode->i_sb->s_pnfs_op = &pnfsd_lexp_ops;
+
+ if (!init_once++)
+ init_waitqueue_head(&lo_recall_wq);
+}
+
+static bool
+has_no_layout(struct nfs4_file *fp)
+{
+ return list_empty(&fp->fi_layouts);
+}
+
+/*
+ * recalls the layout if needed and waits synchronously for its return
+ */
+int
+pnfsd_lexp_recall_layout(struct inode *inode)
+{
+ struct nfs4_file *fp;
+ struct nfsd4_pnfs_cb_layout cbl;
+ struct pnfsd_cb_ctl cb_ctl;
+ int status = 0;
+
+ dprintk("%s: begin\n", __func__);
+ fp = find_file(inode);
+ BUG_ON(!fp);
+
+ if (has_no_layout(fp))
+ goto out;
+
+ memset(&cb_ctl, 0, sizeof(cb_ctl));
+ status = pnfsd_get_cb_op(&cb_ctl);
+ BUG_ON(status);
+
+ memset(&cbl, 0, sizeof(cbl));
+ cbl.cbl_recall_type = RETURN_FILE;
+ cbl.cbl_seg.layout_type = LAYOUT_NFSV4_1_FILES;
+ /* for now, always recall the whole layout */
+ cbl.cbl_seg.iomode = IOMODE_ANY;
+ cbl.cbl_seg.offset = 0;
+ cbl.cbl_seg.length = NFS4_MAX_UINT64;
+
+ while (!has_no_layout(fp)) {
+ dprintk("%s: recalling layout\n", __func__);
+ status = cb_ctl.cb_op->cb_layout_recall(inode->i_sb, inode, &cbl);
+
+ switch (status) {
+ case 0:
+ case -EAGAIN:
+ break;
+ case -ENOENT: /* no matching layout */
+ status = 0;
+ goto out_put_cb;
+ default:
+ goto out_put_cb;
+ }
+
+ dprintk("%s: waiting\n", __func__);
+ status = wait_event_interruptible(lo_recall_wq, has_no_layout(fp));
+ if (status)
+ break;
+ }
+out_put_cb:
+ pnfsd_put_cb_op(&cb_ctl);
+out:
+ put_nfs4_file(fp);
+ dprintk("%s: status=%d\n", __func__, status);
+ return status;
}
@@ -36,6 +36,7 @@
#ifdef CONFIG_NFSD_V4
#include "acl.h"
#include "idmap.h"
+#include "pnfsd.h"
#include <linux/nfsd4_spnfs.h>
#endif /* CONFIG_NFSD_V4 */
#if defined(CONFIG_SPNFS_BLOCK)
@@ -384,6 +385,9 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE);
if (err)
goto out;
+#if defined(CONFIG_PNFSD_LOCAL_EXPORT)
+ pnfsd_lexp_recall_layout(inode);
+#endif /* CONFIG_PNFSD_LOCAL_EXPORT */
#if defined(CONFIG_SPNFS_BLOCK)
if (pnfs_block_enabled(inode, 0)) {
err = bl_layoutrecall(inode, RETURN_FILE,