@@ -127,6 +127,7 @@ static void free_session(struct nfsd4_session *);
127127
128128static const struct nfsd4_callback_ops nfsd4_cb_recall_ops ;
129129static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops ;
130+ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops ;
130131
131132static struct workqueue_struct * laundry_wq ;
132133
@@ -1187,6 +1188,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
11871188 dp -> dl_recalled = false;
11881189 nfsd4_init_cb (& dp -> dl_recall , dp -> dl_stid .sc_client ,
11891190 & nfsd4_cb_recall_ops , NFSPROC4_CLNT_CB_RECALL );
1191+ nfsd4_init_cb (& dp -> dl_cb_fattr .ncf_getattr , dp -> dl_stid .sc_client ,
1192+ & nfsd4_cb_getattr_ops , NFSPROC4_CLNT_CB_GETATTR );
1193+ dp -> dl_cb_fattr .ncf_file_modified = false;
1194+ dp -> dl_cb_fattr .ncf_cb_bmap [0 ] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE ;
11901195 get_nfs4_file (fp );
11911196 dp -> dl_stid .sc_file = fp ;
11921197 return dp ;
@@ -2894,11 +2899,56 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
28942899 spin_unlock (& nn -> client_lock );
28952900}
28962901
2902+ static int
2903+ nfsd4_cb_getattr_done (struct nfsd4_callback * cb , struct rpc_task * task )
2904+ {
2905+ struct nfs4_cb_fattr * ncf =
2906+ container_of (cb , struct nfs4_cb_fattr , ncf_getattr );
2907+
2908+ ncf -> ncf_cb_status = task -> tk_status ;
2909+ switch (task -> tk_status ) {
2910+ case - NFS4ERR_DELAY :
2911+ rpc_delay (task , 2 * HZ );
2912+ return 0 ;
2913+ default :
2914+ return 1 ;
2915+ }
2916+ }
2917+
2918+ static void
2919+ nfsd4_cb_getattr_release (struct nfsd4_callback * cb )
2920+ {
2921+ struct nfs4_cb_fattr * ncf =
2922+ container_of (cb , struct nfs4_cb_fattr , ncf_getattr );
2923+ struct nfs4_delegation * dp =
2924+ container_of (ncf , struct nfs4_delegation , dl_cb_fattr );
2925+
2926+ nfs4_put_stid (& dp -> dl_stid );
2927+ clear_bit (CB_GETATTR_BUSY , & ncf -> ncf_cb_flags );
2928+ wake_up_bit (& ncf -> ncf_cb_flags , CB_GETATTR_BUSY );
2929+ }
2930+
28972931static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
28982932 .done = nfsd4_cb_recall_any_done ,
28992933 .release = nfsd4_cb_recall_any_release ,
29002934};
29012935
2936+ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops = {
2937+ .done = nfsd4_cb_getattr_done ,
2938+ .release = nfsd4_cb_getattr_release ,
2939+ };
2940+
2941+ void nfs4_cb_getattr (struct nfs4_cb_fattr * ncf )
2942+ {
2943+ struct nfs4_delegation * dp =
2944+ container_of (ncf , struct nfs4_delegation , dl_cb_fattr );
2945+
2946+ if (test_and_set_bit (CB_GETATTR_BUSY , & ncf -> ncf_cb_flags ))
2947+ return ;
2948+ refcount_inc (& dp -> dl_stid .sc_count );
2949+ nfsd4_run_cb (& ncf -> ncf_getattr );
2950+ }
2951+
29022952static struct nfs4_client * create_client (struct xdr_netobj name ,
29032953 struct svc_rqst * rqstp , nfs4_verifier * verf )
29042954{
@@ -5634,6 +5684,8 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
56345684 struct svc_fh * parent = NULL ;
56355685 int cb_up ;
56365686 int status = 0 ;
5687+ struct kstat stat ;
5688+ struct path path ;
56375689
56385690 cb_up = nfsd4_cb_channel_good (oo -> oo_owner .so_client );
56395691 open -> op_recall = 0 ;
@@ -5671,6 +5723,18 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
56715723 if (open -> op_share_access & NFS4_SHARE_ACCESS_WRITE ) {
56725724 open -> op_delegate_type = NFS4_OPEN_DELEGATE_WRITE ;
56735725 trace_nfsd_deleg_write (& dp -> dl_stid .sc_stateid );
5726+ path .mnt = currentfh -> fh_export -> ex_path .mnt ;
5727+ path .dentry = currentfh -> fh_dentry ;
5728+ if (vfs_getattr (& path , & stat ,
5729+ (STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE ),
5730+ AT_STATX_SYNC_AS_STAT )) {
5731+ nfs4_put_stid (& dp -> dl_stid );
5732+ destroy_delegation (dp );
5733+ goto out_no_deleg ;
5734+ }
5735+ dp -> dl_cb_fattr .ncf_cur_fsize = stat .size ;
5736+ dp -> dl_cb_fattr .ncf_initial_cinfo =
5737+ nfsd4_change_attribute (& stat , d_inode (currentfh -> fh_dentry ));
56745738 } else {
56755739 open -> op_delegate_type = NFS4_OPEN_DELEGATE_READ ;
56765740 trace_nfsd_deleg_read (& dp -> dl_stid .sc_stateid );
@@ -8407,6 +8471,8 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
84078471 * nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
84088472 * @rqstp: RPC transaction context
84098473 * @inode: file to be checked for a conflict
8474+ * @modified: return true if file was modified
8475+ * @size: new size of file if modified is true
84108476 *
84118477 * This function is called when there is a conflict between a write
84128478 * delegation and a change/size GETATTR from another client. The server
@@ -8415,21 +8481,23 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
84158481 * delegation before replying to the GETATTR. See RFC 8881 section
84168482 * 18.7.4.
84178483 *
8418- * The current implementation does not support CB_GETATTR yet. However
8419- * this can avoid recalling the delegation could be added in follow up
8420- * work.
8421- *
84228484 * Returns 0 if there is no conflict; otherwise an nfs_stat
84238485 * code is returned.
84248486 */
84258487__be32
8426- nfsd4_deleg_getattr_conflict (struct svc_rqst * rqstp , struct inode * inode )
8488+ nfsd4_deleg_getattr_conflict (struct svc_rqst * rqstp , struct inode * inode ,
8489+ bool * modified , u64 * size )
84278490{
8428- __be32 status ;
84298491 struct file_lock_context * ctx ;
8430- struct file_lock * fl ;
84318492 struct nfs4_delegation * dp ;
8493+ struct nfs4_cb_fattr * ncf ;
8494+ struct file_lock * fl ;
8495+ struct iattr attrs ;
8496+ __be32 status ;
8497+
8498+ might_sleep ();
84328499
8500+ * modified = false;
84338501 ctx = locks_inode_context (inode );
84348502 if (!ctx )
84358503 return 0 ;
@@ -8456,10 +8524,34 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode)
84568524break_lease :
84578525 spin_unlock (& ctx -> flc_lock );
84588526 nfsd_stats_wdeleg_getattr_inc ();
8459- status = nfserrno (nfsd_open_break_lease (inode , NFSD_MAY_READ ));
8460- if (status != nfserr_jukebox ||
8461- !nfsd_wait_for_delegreturn (rqstp , inode ))
8462- return status ;
8527+
8528+ dp = fl -> fl_owner ;
8529+ ncf = & dp -> dl_cb_fattr ;
8530+ nfs4_cb_getattr (& dp -> dl_cb_fattr );
8531+ wait_on_bit (& ncf -> ncf_cb_flags , CB_GETATTR_BUSY , TASK_INTERRUPTIBLE );
8532+ if (ncf -> ncf_cb_status ) {
8533+ status = nfserrno (nfsd_open_break_lease (inode , NFSD_MAY_READ ));
8534+ if (status != nfserr_jukebox ||
8535+ !nfsd_wait_for_delegreturn (rqstp , inode ))
8536+ return status ;
8537+ }
8538+ if (!ncf -> ncf_file_modified &&
8539+ (ncf -> ncf_initial_cinfo != ncf -> ncf_cb_change ||
8540+ ncf -> ncf_cur_fsize != ncf -> ncf_cb_fsize ))
8541+ ncf -> ncf_file_modified = true;
8542+ if (ncf -> ncf_file_modified ) {
8543+ /*
8544+ * The server would not update the file's metadata
8545+ * with the client's modified size.
8546+ */
8547+ attrs .ia_mtime = attrs .ia_ctime = current_time (inode );
8548+ attrs .ia_valid = ATTR_MTIME | ATTR_CTIME ;
8549+ setattr_copy (& nop_mnt_idmap , inode , & attrs );
8550+ mark_inode_dirty (inode );
8551+ ncf -> ncf_cur_fsize = ncf -> ncf_cb_fsize ;
8552+ * size = ncf -> ncf_cur_fsize ;
8553+ * modified = true;
8554+ }
84638555 return 0 ;
84648556 }
84658557 break ;
0 commit comments