Motr  M0
fsync.c
Go to the documentation of this file.
1 /* -*- C -*- */
2 /*
3  * Copyright (c) 2014-2020 Seagate Technology LLC and/or its Affiliates
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * For any questions about this software or licensing,
18  * please email opensource@seagate.com or cortx-questions@seagate.com.
19  *
20  */
21 
22 
23 #include <linux/version.h> /* LINUX_VERSION_CODE */
24 #include <linux/fs.h> /* struct file_operations */
25 #include <linux/mount.h> /* struct vfsmount (f_path.mnt) */
26 
27 #define M0_TRACE_SUBSYSTEM M0_TRACE_SUBSYS_M0T1FS
28 #include "lib/trace.h"
29 #include "mdservice/fsync_fops.h" /* m0_fop_fsync_mds_fopt */
30 #include "fop/fom_generic.h" /* m0_rpc_item_is_generic_reply_fop */
31 #include "lib/memory.h" /* m0_alloc, m0_free */
32 #include "lib/tlist.h"
33 #include "lib/hash.h" /* m0_htable */
34 #include "file/file.h"
35 #include "motr/magic.h" /* M0_T1FS_FFW_TLIST_MAGIC? */
36 #include "m0t1fs/linux_kernel/m0t1fs.h" /* m0t1fs_sb */
37 
38 #include "layout/pdclust.h" /* M0_PUT_*, m0_layout_to_pdl,
39  * m0_pdclust_instance_map */
41 
42 #include "m0t1fs/linux_kernel/fsync.h" /* m0t1fs_fsync_interactions */
43 
44 M0_TL_DESCR_DEFINE(fpf, "m0t1fs_fsync_fop_wrappers pending fsync-fops",
45  static, struct m0t1fs_fsync_fop_wrapper, ffw_tlink,
46  ffw_tlink_magic, M0_T1FS_FFW_TLIST_MAGIC1,
48 
49 M0_TL_DEFINE(fpf, static, struct m0t1fs_fsync_fop_wrapper);
50 
56 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
57 /*
58  * Starting from 3.16 generic_file_fsync() tries to flush block
59  * device by calling blkdev_issue_flush(inode->i_sb->s_bdev), but
60  * we don't have any block device.
61  */
62 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
63  .kernel_fsync = __generic_file_fsync,
64 #else
65  .kernel_fsync = generic_file_fsync,
66 #endif
67 #else
68  .kernel_fsync = &simple_fsync,
69 #endif
70  .post_rpc = &m0_rpc_post,
71  .wait_for_reply = &m0_rpc_item_wait_for_reply,
72  /* fini is for requests, allocated in a bigger structure */
73  .fop_fini = &m0_fop_fini,
74  /* put is for replies, allocated by a lower layer */
75  .fop_put = &m0_fop_put_lock,
76 };
77 
78 
82 static void m0t1fs_fsync_fop_cleanup(struct m0_ref *ref)
83 {
84  struct m0_fop *fop;
85  struct m0t1fs_fsync_fop_wrapper *ffw;
86 
87  M0_ENTRY();
88  M0_PRE(fi.fop_fini != NULL);
89 
90  fop = container_of(ref, struct m0_fop, f_ref);
91  fi.fop_fini(fop);
92 
94  m0_free(ffw);
95 
96  M0_LEAVE();
97 }
98 
99 
106  struct m0t1fs_fsync_fop_wrapper **ffw_out,
107  enum m0_fsync_mode mode)
108 {
109  int rc;
110  struct m0_fop *fop;
111  struct m0_rpc_item *item;
112  struct m0_fop_fsync *ffd;
113  struct m0_fop_type *fopt;
114  struct m0t1fs_fsync_fop_wrapper *ffw;
115 
116  M0_ENTRY();
117 
118  M0_ALLOC_PTR(ffw);
119  if (ffw == NULL)
120  return M0_ERR(-ENOMEM);
121 
123  if (rc != 0) {
124  m0_free(ffw);
125  return M0_ERR(rc);
126  }
127 
128  if (stx->stx_service_ctx->sc_type == M0_CST_MDS)
129  fopt = &m0_fop_fsync_mds_fopt;
130  else if (stx->stx_service_ctx->sc_type == M0_CST_IOS)
131  fopt = &m0_fop_fsync_ios_fopt;
132  else
133  M0_IMPOSSIBLE("invalid service type:%d",
135 
136  /* store the pending txid reference with the fop */
137  ffw->ffw_stx = stx;
138 
139  fop = &ffw->ffw_fop;
142  if (rc != 0) {
143  m0_free(ffw);
144  return M0_ERR_INFO(rc, "Allocating fsync fop data failed.");
145  }
146 
147  ffd = m0_fop_data(fop);
148 
149  ffd->ff_be_remid = stx->stx_tri;
150  ffd->ff_fsync_mode = mode;
151 
152  /*
153  * We post the rpc_item directly so that this is asyncronous.
154  * Prepare the fop as an rpc item
155  */
156  item = &fop->f_item;
159  item->ri_deadline = 0;
160  item->ri_nr_sent_max = M0T1FS_RPC_MAX_RETRIES;
161  item->ri_resend_interval = M0T1FS_RPC_RESEND_INTERVAL;
162 
163  rc = fi.post_rpc(item);
164  if (rc != 0) {
165  fi.fop_put(fop);
166  return M0_ERR_INFO(rc, "Calling m0_rpc_post() failed.");
167  }
168 
169  *ffw_out = ffw;
170  M0_LEAVE();
171  return 0;
172 }
173 
174 static void fsync_stx_update(struct m0_reqh_service_txid *stx, uint64_t txid,
175  struct m0_mutex *lock)
176 {
177  M0_PRE(stx != NULL);
178  M0_PRE(lock != NULL);
179 
181  if (stx->stx_tri.tri_txid <= txid) {
182  /*
183  * Our transaction got committed update the record to be
184  * ignored in the future.
185  */
186  M0_SET0(&stx->stx_tri);
187  }
188  /*
189  * Else the stx_maximum_txid got increased while we were waiting, it
190  * is acceptable for fsync to return as long as the
191  * correct-at-time-of-sending txn was committed (which the caller
192  * should assert).
193  */
195 }
196 
197 
206 int m0t1fs_fsync_reply_process(struct m0t1fs_sb *csb,
207  struct m0t1fs_inode *inode,
208  struct m0t1fs_fsync_fop_wrapper *ffw)
209 {
210  int rc;
211  uint64_t reply_txid;
212  struct m0_fop *fop;
213  struct m0_rpc_item *item;
214  struct m0_fop_fsync *ffd;
215  struct m0_fop_fsync_rep *ffr;
217 
218  M0_ENTRY();
219 
220  if (csb == NULL)
222  M0_PRE(csb != NULL);
223 
224  fop = &ffw->ffw_fop;
225  item = &fop->f_item;
226 
227  rc = fi.wait_for_reply(item, m0_time_from_now(M0T1FS_RPC_TIMEOUT, 0));
228  if (rc != 0)
229  goto out;
230 
231  /* get the {fop,reply} data */
232  ffd = m0_fop_data(fop);
233  M0_ASSERT(ffd != NULL);
235  M0_ASSERT(ffr != NULL);
236 
237  rc = ffr->ffr_rc;
238  if (rc != 0) {
239  M0_LOG(M0_ERROR, "reply rc=%d", rc);
240  goto out;
241  }
242 
243  /* Is this a valid reply to our request */
244  reply_txid = ffr->ffr_be_remid.tri_txid;
245  if (reply_txid < ffd->ff_be_remid.tri_txid) {
246  rc = -EPROTO;
247  M0_LOG(M0_ERROR, "Commited transaction is smaller "
248  "than that requested.");
249  goto out;
250  }
251 
252  if (inode != NULL)
253  fsync_stx_update(ffw->ffw_stx, reply_txid,
254  &inode->ci_pending_tx_lock);
255 
257 
258  /*
259  * check the super block too, super block txid_record
260  * is embedded in the m0_reqh_service_ctx struct
261  */
264 out:
265  fi.fop_put(fop);
266 
267  return M0_RC(rc);
268 }
269 
270 
281 int m0t1fs_fsync_core(struct m0t1fs_inode *inode, enum m0_fsync_mode mode)
282 {
283  int rc;
284  int saved_error = 0;
285  struct m0_tl pending_fops;
286  struct m0_reqh_service_txid *iter;
287  struct m0t1fs_fsync_fop_wrapper *ffw;
288 
289  M0_ENTRY();
290 
291  M0_PRE(inode != NULL);
293  M0_PRE(fi.post_rpc != NULL);
295  M0_PRE(fi.fop_fini != NULL);
296 
297  m0_tlist_init(&fpf_tl, &pending_fops);
298 
299  /*
300  * find the inode's list services with pending transactions
301  * for each entry, send an fsync fop.
302  * This is the fop sending loop.
303  */
304  m0_mutex_lock(&inode->ci_pending_tx_lock);
305  m0_tl_for(ispti, &inode->ci_pending_tx, iter) {
306  /*
307  * send an fsync fop for
308  * iter->stx_maximum_txid to iter->stx_service_ctx
309  */
310 
311  /* Check if this service has any pending transactions. */
312  if (iter->stx_tri.tri_txid == 0)
313  continue;
314 
315  /* Create and send a request */
316  rc = m0t1fs_fsync_request_create(iter, &ffw, mode);
317  if (rc != 0) {
318  saved_error = rc;
319  break;
320  } else
321  /* Add to list of pending fops */
322  fpf_tlink_init_at(ffw, &pending_fops);
323  } m0_tl_endfor;
324  m0_mutex_unlock(&inode->ci_pending_tx_lock);
325 
326  /*
327  * At this point we may have sent some fops, but stopped when one
328  * failed - collect all the replies before returning.
329  */
330 
331  /* This is the fop-reply receiving loop. */
332  m0_tl_teardown(fpf, &pending_fops, ffw) {
333  /* Get and process the reply. */
335  saved_error = saved_error ? : rc;
336  }
337 
338  M0_LEAVE();
339  return saved_error;
340 }
341 
342 
353 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
354 int m0t1fs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
355 #else
356 int m0t1fs_fsync(struct file *file, struct dentry *dentry, int datasync)
357 #endif
358 {
359  int rc;
360  struct m0t1fs_inode *inode;
361 
363  M0_ENTRY();
364 
365  M0_PRE(file != NULL);
367  M0_PRE(inode != NULL);
368 
369  /*
370  * push any relevant changes we don't know about through m0t1fs_aio
371  * This call will block until all the data is sent to the server, and
372  * we have uptodate pending transaction-ids.
373  */
374 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
375  rc = fi.kernel_fsync(file, start, end, datasync);
376 #else
377  rc = fi.kernel_fsync(file, dentry, datasync);
378 #endif
379  if (rc != 0) {
384  return M0_ERR_INFO(rc, "Simple_fsync returned error.");
385  }
386 
387  M0_LEAVE();
389 }
390 
398  struct m0t1fs_sb *csb,
399  struct m0t1fs_inode *inode,
400  struct m0_be_tx_remid *btr)
401 {
402  struct m0_reqh_service_txid *stx = NULL;
403 
404  M0_ENTRY();
405 
406  M0_PRE(service != NULL);
407  if (csb == NULL)
409  M0_PRE(csb != NULL);
410 
411  /* Updates pending transaction number in the inode */
412  if (inode != NULL) {
413  /*
414  * TODO: replace this O(N) search with something better.
415  * Embbed the struct m0_reqh_service_txid in a list of
416  * 'services for this inode'? See RB1667
417  */
418  /* Find the record for this service */
419  m0_mutex_lock(&inode->ci_pending_tx_lock);
420  stx = m0_tl_find(ispti, stx,
421  &inode->ci_pending_tx,
423 
424  if (stx != NULL) {
425  if (btr->tri_txid > stx->stx_tri.tri_txid)
426  stx->stx_tri = *btr;
427  } else {
428  /*
429  * not found - add a new record
430  */
431  M0_ALLOC_PTR(stx);
432  if (stx != NULL) {
434  stx->stx_tri = *btr;
435 
436  ispti_tlink_init_at(stx, &inode->ci_pending_tx);
437  }
438  }
439  m0_mutex_unlock(&inode->ci_pending_tx_lock);
440  }
441 
442  /* update pending transaction number in the super block */
445  /* update the value from the reply_fop */
446  if (btr->tri_txid > stx->stx_tri.tri_txid) {
448  stx->stx_tri = *btr;
449  }
451 
452  M0_LEAVE("Fsync record updated.");
453 }
454 
455 
463 {
464  int rc;
465  int saved_error = 0;
466  struct m0_tl pending_fops;
467  struct m0_reqh_service_txid *stx;
468  struct m0_reqh_service_ctx *iter;
469  struct m0t1fs_fsync_fop_wrapper *ffw;
470  struct m0t1fs_sb *csb;
471 
473  M0_ENTRY();
474 
476  M0_PRE(fi.post_rpc != NULL);
478  M0_PRE(fi.fop_fini != NULL);
479 
480  csb = M0T1FS_SB(sb);
481 
482  m0_tlist_init(&fpf_tl, &pending_fops);
483 
484  /*
485  * loop over all services associated with this super block,
486  * send an fsync fop for those with pending transactions
487  *
488  * fop sending loop
489  */
490  m0_tl_for(pools_common_svc_ctx, &csb->csb_pools_common.pc_svc_ctxs,
491  iter) {
492  /*
493  * Send an fsync fop for iter->sc_max_pending_txt to iter.
494  */
496  stx = &iter->sc_max_pending_tx;
497 
498  /*
499  * Check if this service has any pending transactions.
500  * Currently for fsync operations are supported only for
501  * ioservice and mdservice.
502  */
503  if (stx->stx_tri.tri_txid == 0 ||
504  !M0_IN(stx->stx_service_ctx->sc_type,
505  (M0_CST_MDS, M0_CST_IOS))) {
507  continue;
508  }
509 
510  /* Create and send a request */
513  if (rc != 0) {
514  saved_error = rc;
516  break;
517  } else {
518  /* Add to list of pending fops */
519  fpf_tlink_init_at(ffw, &pending_fops);
520  }
521 
523  } m0_tl_endfor;
524 
525  /*
526  * At this point we may have sent some fops, but stopped when one
527  * failed - collect all the replies before returning
528  */
529 
530  /* reply receiving loop */
531  m0_tl_teardown(fpf, &pending_fops, ffw) {
532  /* get and process the reply */
534  saved_error = saved_error ? : rc;
535  }
536 
537  M0_LEAVE();
538 
539  return saved_error;
540 }
M0_INTERNAL int m0_rpc_post(struct m0_rpc_item *item)
Definition: rpc.c:63
static struct m0_mutex lock
Definition: transmit.c:326
static struct m0_reqh_service_txid stx[NUM_STRECORDS]
Definition: fsync.c:71
m0_time_t ri_resend_interval
Definition: item.h:144
#define M0_PRE(cond)
static struct m0_semaphore wait
Definition: item.c:151
struct m0t1fs_fsync_interactions fi
Definition: fsync.c:55
void(* fop_put)(struct m0_fop *fop)
Definition: fsync.h:388
M0_INTERNAL void m0_mutex_unlock(struct m0_mutex *mutex)
Definition: mutex.c:66
enum m0_rpc_item_priority ri_prio
Definition: item.h:133
#define NULL
Definition: misc.h:38
struct m0_mutex sc_max_pending_tx_lock
Definition: reqh_service.h:773
struct m0_file file
Definition: di.c:36
M0_INTERNAL void m0_fop_init(struct m0_fop *fop, struct m0_fop_type *fopt, void *data, void(*fop_release)(struct m0_ref *))
Definition: fop.c:79
#define M0_LOG(level,...)
Definition: trace.h:167
M0_LEAVE()
int(* post_rpc)(struct m0_rpc_item *item)
Definition: fsync.h:385
struct m0_fop_type m0_fop_fsync_ios_fopt
Definition: io_fops.c:82
M0_INTERNAL void m0_tlist_init(const struct m0_tl_descr *d, struct m0_tl *list)
Definition: tlist.c:46
void * m0_fop_data(const struct m0_fop *fop)
Definition: fop.c:220
#define container_of(ptr, type, member)
Definition: misc.h:33
#define M0_SET0(obj)
Definition: misc.h:64
M0_INTERNAL void m0_mutex_lock(struct m0_mutex *mutex)
Definition: mutex.c:49
int32_t ffr_rc
Definition: fsync_fops.h:64
struct m0t1fs_sb * csb
Definition: dir.c:330
struct m0_fop_type m0_fop_fsync_mds_fopt
Definition: fsync_fops.c:33
static struct m0_rpc_item * item
Definition: item.c:56
int m0_rpc_item_wait_for_reply(struct m0_rpc_item *item, m0_time_t timeout)
Definition: item.c:824
struct inode * inode
Definition: dir.c:624
#define m0_tl_endfor
Definition: tlist.h:700
M0_TL_DESCR_DEFINE(fpf, "m0t1fs_fsync_fop_wrappers pending fsync-fops", static, struct m0t1fs_fsync_fop_wrapper, ffw_tlink, ffw_tlink_magic, M0_T1FS_FFW_TLIST_MAGIC1, M0_T1FS_FFW_TLIST_MAGIC2)
return M0_RC(rc)
#define M0_ENTRY(...)
Definition: trace.h:170
#define M0_ERR_INFO(rc, fmt,...)
Definition: trace.h:215
return M0_ERR(-EOPNOTSUPP)
Definition: refs.h:34
#define m0_tl_teardown(name, head, obj)
Definition: tlist.h:708
#define M0_ASSERT(cond)
static int struct dentry * dentry
Definition: dir.c:589
M0_THREAD_ENTER
Definition: dir.c:336
struct m0_be_tx_remid stx_tri
Definition: reqh_service.h:739
M0_INTERNAL struct m0t1fs_inode * m0t1fs_file_to_m0inode(const struct file *file)
Definition: file.c:444
struct m0_be_tx_remid ffr_be_remid
Definition: fsync_fops.h:70
Definition: tlist.h:251
int m0t1fs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
Definition: fsync.c:354
static void m0t1fs_fsync_fop_cleanup(struct m0_ref *ref)
Definition: fsync.c:82
struct m0_reqh_service_ctx * stx_service_ctx
Definition: reqh_service.h:733
enum m0_conf_service_type sc_type
Definition: reqh_service.h:757
struct m0_rpc_item * ri_reply
Definition: item.h:163
uint64_t ri_nr_sent_max
Definition: item.h:146
static int struct dentry int mode
Definition: dir.c:589
int m0t1fs_sync_fs(struct super_block *sb, int wait)
Definition: fsync.c:462
static struct super_block super_block
Definition: fsync.c:84
M0_INTERNAL int m0_fop_data_alloc(struct m0_fop *fop)
Definition: fop.c:71
M0_INTERNAL void m0_fop_fini(struct m0_fop *fop)
Definition: fop.c:136
struct m0_be_tx_remid ff_be_remid
Definition: fsync_fops.h:115
uint64_t tri_txid
Definition: tx.h:431
static struct m0_reqh_service_ctx service
Definition: fsync.c:93
M0_INTERNAL int m0_rpc_session_validate(struct m0_rpc_session *session)
Definition: session.c:573
static struct super_block sb
Definition: file.c:85
void(* fop_fini)(struct m0_fop *fop)
Definition: fsync.h:387
int m0t1fs_fsync_request_create(struct m0_reqh_service_txid *stx, struct m0t1fs_fsync_fop_wrapper **ffw_out, enum m0_fsync_mode mode)
Definition: fsync.c:105
struct m0_be_tx_remid ff_be_remid
Definition: fsync_fops.h:48
#define M0_ALLOC_PTR(ptr)
Definition: memory.h:86
m0_time_t m0_time_from_now(uint64_t secs, long ns)
Definition: time.c:96
struct m0_rpc_session * ri_session
Definition: item.h:147
int(* kernel_fsync)(struct file *file, loff_t start, loff_t end, int datasync)
Definition: fsync.h:379
static void fsync_stx_update(struct m0_reqh_service_txid *stx, uint64_t txid, struct m0_mutex *lock)
Definition: fsync.c:174
static int start(struct m0_fom *fom)
Definition: trigger_fom.c:321
void m0_fop_put_lock(struct m0_fop *fop)
Definition: fop.c:199
static struct m0_fop * fop
Definition: item.c:57
struct m0_fop * m0_rpc_item_to_fop(const struct m0_rpc_item *item)
Definition: fop.c:346
m0_fsync_mode
Definition: fsync_fops.h:54
struct m0_reqh_service_txid * ffw_stx
Definition: fsync.h:366
uint32_t ff_fsync_mode
Definition: fsync_fops.h:51
#define out(...)
Definition: gen.c:41
struct m0_fop ffw_fop
Definition: fsync.h:358
struct m0_rpc_link sc_rlink
Definition: reqh_service.h:759
#define m0_tl_find(name, var, head,...)
Definition: tlist.h:757
#define m0_tl_for(name, head, obj)
Definition: tlist.h:695
void m0_free(void *data)
Definition: memory.c:146
Definition: mutex.h:47
struct m0_rpc_item f_item
Definition: fop.h:83
int32_t rc
Definition: trigger_fop.h:47
M0_INTERNAL struct m0t1fs_sb * m0inode_to_sb(const struct m0t1fs_inode *m0inode)
Definition: file.c:472
struct m0_reqh_service_txid sc_max_pending_tx
Definition: reqh_service.h:772
Definition: fop.h:79
void m0t1fs_fsync_record_update(struct m0_reqh_service_ctx *service, struct m0t1fs_sb *csb, struct m0t1fs_inode *inode, struct m0_be_tx_remid *btr)
Definition: fsync.c:397
int m0t1fs_fsync_reply_process(struct m0t1fs_sb *csb, struct m0t1fs_inode *inode, struct m0t1fs_fsync_fop_wrapper *ffw)
Definition: fsync.c:206
int(* wait_for_reply)(struct m0_rpc_item *item, m0_time_t timeout)
Definition: fsync.h:386
int m0t1fs_fsync_core(struct m0t1fs_inode *inode, enum m0_fsync_mode mode)
Definition: fsync.c:281
m0_time_t ri_deadline
Definition: item.h:141
#define M0_IMPOSSIBLE(fmt,...)
M0_TL_DEFINE(fpf, static, struct m0t1fs_fsync_fop_wrapper)