@@ -127,6 +127,7 @@ static void free_session(struct nfsd4_session *);
127
127
128
128
static const struct nfsd4_callback_ops nfsd4_cb_recall_ops ;
129
129
static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops ;
130
+ static const struct nfsd4_callback_ops nfsd4_cb_getattr_ops ;
130
131
131
132
static struct workqueue_struct * laundry_wq ;
132
133
@@ -1187,6 +1188,10 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
1187
1188
dp -> dl_recalled = false;
1188
1189
nfsd4_init_cb (& dp -> dl_recall , dp -> dl_stid .sc_client ,
1189
1190
& 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 ;
1190
1195
get_nfs4_file (fp );
1191
1196
dp -> dl_stid .sc_file = fp ;
1192
1197
return dp ;
@@ -2894,11 +2899,56 @@ nfsd4_cb_recall_any_release(struct nfsd4_callback *cb)
2894
2899
spin_unlock (& nn -> client_lock );
2895
2900
}
2896
2901
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
+
2897
2931
static const struct nfsd4_callback_ops nfsd4_cb_recall_any_ops = {
2898
2932
.done = nfsd4_cb_recall_any_done ,
2899
2933
.release = nfsd4_cb_recall_any_release ,
2900
2934
};
2901
2935
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
+
2902
2952
static struct nfs4_client * create_client (struct xdr_netobj name ,
2903
2953
struct svc_rqst * rqstp , nfs4_verifier * verf )
2904
2954
{
@@ -5634,6 +5684,8 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
5634
5684
struct svc_fh * parent = NULL ;
5635
5685
int cb_up ;
5636
5686
int status = 0 ;
5687
+ struct kstat stat ;
5688
+ struct path path ;
5637
5689
5638
5690
cb_up = nfsd4_cb_channel_good (oo -> oo_owner .so_client );
5639
5691
open -> op_recall = 0 ;
@@ -5671,6 +5723,18 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
5671
5723
if (open -> op_share_access & NFS4_SHARE_ACCESS_WRITE ) {
5672
5724
open -> op_delegate_type = NFS4_OPEN_DELEGATE_WRITE ;
5673
5725
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 ));
5674
5738
} else {
5675
5739
open -> op_delegate_type = NFS4_OPEN_DELEGATE_READ ;
5676
5740
trace_nfsd_deleg_read (& dp -> dl_stid .sc_stateid );
@@ -8407,6 +8471,8 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
8407
8471
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
8408
8472
* @rqstp: RPC transaction context
8409
8473
* @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
8410
8476
*
8411
8477
* This function is called when there is a conflict between a write
8412
8478
* delegation and a change/size GETATTR from another client. The server
@@ -8415,21 +8481,23 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
8415
8481
* delegation before replying to the GETATTR. See RFC 8881 section
8416
8482
* 18.7.4.
8417
8483
*
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
- *
8422
8484
* Returns 0 if there is no conflict; otherwise an nfs_stat
8423
8485
* code is returned.
8424
8486
*/
8425
8487
__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 )
8427
8490
{
8428
- __be32 status ;
8429
8491
struct file_lock_context * ctx ;
8430
- struct file_lock * fl ;
8431
8492
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 ();
8432
8499
8500
+ * modified = false;
8433
8501
ctx = locks_inode_context (inode );
8434
8502
if (!ctx )
8435
8503
return 0 ;
@@ -8456,10 +8524,34 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct inode *inode)
8456
8524
break_lease :
8457
8525
spin_unlock (& ctx -> flc_lock );
8458
8526
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
+ }
8463
8555
return 0 ;
8464
8556
}
8465
8557
break ;
0 commit comments