]> Git Repo - linux.git/blame - fs/orangefs/file.c
orangefs: implement writepage
[linux.git] / fs / orangefs / file.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
5db11c21
MM
2/*
3 * (C) 2001 Clemson University and The University of Chicago
85ac799c 4 * Copyright 2018 Omnibond Systems, L.L.C.
5db11c21
MM
5 *
6 * See COPYING in top-level directory.
7 */
8
9/*
10 * Linux VFS file operations.
11 */
12
13#include "protocol.h"
575e9461
MM
14#include "orangefs-kernel.h"
15#include "orangefs-bufmap.h"
5db11c21
MM
16#include <linux/fs.h>
17#include <linux/pagemap.h>
18
ed1e1587
MB
19static int flush_racache(struct inode *inode)
20{
21 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
22 struct orangefs_kernel_op_s *new_op;
23 int ret;
24
25 gossip_debug(GOSSIP_UTILS_DEBUG,
26 "%s: %pU: Handle is %pU | fs_id %d\n", __func__,
27 get_khandle_from_ino(inode), &orangefs_inode->refn.khandle,
28 orangefs_inode->refn.fs_id);
29
30 new_op = op_alloc(ORANGEFS_VFS_OP_RA_FLUSH);
31 if (!new_op)
32 return -ENOMEM;
33 new_op->upcall.req.ra_cache_flush.refn = orangefs_inode->refn;
34
35 ret = service_operation(new_op, "orangefs_flush_racache",
36 get_interruptible_flag(inode));
37
38 gossip_debug(GOSSIP_UTILS_DEBUG, "%s: got return value of %d\n",
39 __func__, ret);
40
41 op_release(new_op);
42 return ret;
43}
44
5db11c21
MM
45/*
46 * Post and wait for the I/O upcall to finish
47 */
c453dcfc 48ssize_t wait_for_direct_io(enum ORANGEFS_io_type type, struct inode *inode,
3c2fcfcb 49 loff_t *offset, struct iov_iter *iter,
4d1c4404 50 size_t total_size, loff_t readahead_size)
5db11c21 51{
8bb8aefd
YL
52 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
53 struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
8bb8aefd 54 struct orangefs_kernel_op_s *new_op = NULL;
5db11c21
MM
55 int buffer_index = -1;
56 ssize_t ret;
57
8bb8aefd 58 new_op = op_alloc(ORANGEFS_VFS_OP_FILE_IO);
ed42fe05
AV
59 if (!new_op)
60 return -ENOMEM;
61
5db11c21 62 /* synchronous I/O */
5db11c21
MM
63 new_op->upcall.req.io.readahead_size = readahead_size;
64 new_op->upcall.req.io.io_type = type;
8bb8aefd 65 new_op->upcall.req.io.refn = orangefs_inode->refn;
5db11c21
MM
66
67populate_shared_memory:
68 /* get a shared buffer index */
b8a99a8f
AV
69 buffer_index = orangefs_bufmap_get();
70 if (buffer_index < 0) {
71 ret = buffer_index;
5db11c21 72 gossip_debug(GOSSIP_FILE_DEBUG,
b8a99a8f
AV
73 "%s: orangefs_bufmap_get failure (%zd)\n",
74 __func__, ret);
5db11c21
MM
75 goto out;
76 }
77 gossip_debug(GOSSIP_FILE_DEBUG,
78 "%s(%pU): GET op %p -> buffer_index %d\n",
79 __func__,
80 handle,
81 new_op,
82 buffer_index);
83
84 new_op->uses_shared_memory = 1;
85 new_op->upcall.req.io.buf_index = buffer_index;
86 new_op->upcall.req.io.count = total_size;
87 new_op->upcall.req.io.offset = *offset;
88
89 gossip_debug(GOSSIP_FILE_DEBUG,
3c2fcfcb 90 "%s(%pU): offset: %llu total_size: %zd\n",
5db11c21
MM
91 __func__,
92 handle,
5db11c21
MM
93 llu(*offset),
94 total_size);
95 /*
96 * Stage 1: copy the buffers into client-core's address space
5db11c21 97 */
dbcb5e7f
MB
98 if (type == ORANGEFS_IO_WRITE && total_size) {
99 ret = orangefs_bufmap_copy_from_iovec(iter, buffer_index,
100 total_size);
101 if (ret < 0) {
102 gossip_err("%s: Failed to copy-in buffers. Please make sure that the pvfs2-client is running. %ld\n",
103 __func__, (long)ret);
5db11c21 104 goto out;
dbcb5e7f 105 }
5db11c21
MM
106 }
107
108 gossip_debug(GOSSIP_FILE_DEBUG,
109 "%s(%pU): Calling post_io_request with tag (%llu)\n",
110 __func__,
111 handle,
112 llu(new_op->tag));
113
114 /* Stage 2: Service the I/O operation */
115 ret = service_operation(new_op,
8bb8aefd 116 type == ORANGEFS_IO_WRITE ?
5db11c21
MM
117 "file_write" :
118 "file_read",
119 get_interruptible_flag(inode));
120
121 /*
122 * If service_operation() returns -EAGAIN #and# the operation was
8bb8aefd 123 * purged from orangefs_request_list or htable_ops_in_progress, then
5db11c21
MM
124 * we know that the client was restarted, causing the shared memory
125 * area to be wiped clean. To restart a write operation in this
126 * case, we must re-copy the data from the user's iovec to a NEW
127 * shared memory location. To restart a read operation, we must get
128 * a new shared memory location.
129 */
130 if (ret == -EAGAIN && op_state_purged(new_op)) {
1357d06d 131 orangefs_bufmap_put(buffer_index);
e17be9fd 132 buffer_index = -1;
7b9761af 133 if (type == ORANGEFS_IO_WRITE)
c63ed807 134 iov_iter_revert(iter, total_size);
5db11c21
MM
135 gossip_debug(GOSSIP_FILE_DEBUG,
136 "%s:going to repopulate_shared_memory.\n",
137 __func__);
138 goto populate_shared_memory;
139 }
140
141 if (ret < 0) {
162ada77
MM
142 if (ret == -EINTR) {
143 /*
144 * We can't return EINTR if any data was written,
145 * it's not POSIX. It is minimally acceptable
146 * to give a partial write, the way NFS does.
147 *
148 * It would be optimal to return all or nothing,
149 * but if a userspace write is bigger than
150 * an IO buffer, and the interrupt occurs
151 * between buffer writes, that would not be
152 * possible.
153 */
154 switch (new_op->op_state - OP_VFS_STATE_GIVEN_UP) {
155 /*
156 * If the op was waiting when the interrupt
157 * occurred, then the client-core did not
158 * trigger the write.
159 */
160 case OP_VFS_STATE_WAITING:
161 if (*offset == 0)
162 ret = -EINTR;
163 else
164 ret = 0;
165 break;
95f5f88f 166 /*
162ada77
MM
167 * If the op was in progress when the interrupt
168 * occurred, then the client-core was able to
169 * trigger the write.
170 */
171 case OP_VFS_STATE_INPROGR:
172 ret = total_size;
173 break;
174 default:
175 gossip_err("%s: unexpected op state :%d:.\n",
176 __func__,
177 new_op->op_state);
178 ret = 0;
179 break;
180 }
5db11c21 181 gossip_debug(GOSSIP_FILE_DEBUG,
162ada77
MM
182 "%s: got EINTR, state:%d: %p\n",
183 __func__,
184 new_op->op_state,
185 new_op);
186 } else {
5db11c21
MM
187 gossip_err("%s: error in %s handle %pU, returning %zd\n",
188 __func__,
8bb8aefd 189 type == ORANGEFS_IO_READ ?
5db11c21
MM
190 "read from" : "write to",
191 handle, ret);
162ada77 192 }
78699e29
AV
193 if (orangefs_cancel_op_in_progress(new_op))
194 return ret;
195
897c5df6 196 goto out;
5db11c21
MM
197 }
198
199 /*
200 * Stage 3: Post copy buffers from client-core's address space
5db11c21 201 */
dbcb5e7f
MB
202 if (type == ORANGEFS_IO_READ && new_op->downcall.resp.io.amt_complete) {
203 /*
204 * NOTE: the iovector can either contain addresses which
205 * can futher be kernel-space or user-space addresses.
206 * or it can pointers to struct page's
207 */
208 ret = orangefs_bufmap_copy_to_iovec(iter, buffer_index,
209 new_op->downcall.resp.io.amt_complete);
210 if (ret < 0) {
211 gossip_err("%s: Failed to copy-out buffers. Please make sure that the pvfs2-client is running (%ld)\n",
212 __func__, (long)ret);
897c5df6 213 goto out;
dbcb5e7f 214 }
5db11c21
MM
215 }
216 gossip_debug(GOSSIP_FILE_DEBUG,
9d9e7ba9 217 "%s(%pU): Amount %s, returned by the sys-io call:%d\n",
5db11c21
MM
218 __func__,
219 handle,
9d9e7ba9 220 type == ORANGEFS_IO_READ ? "read" : "written",
5db11c21
MM
221 (int)new_op->downcall.resp.io.amt_complete);
222
223 ret = new_op->downcall.resp.io.amt_complete;
224
5db11c21
MM
225out:
226 if (buffer_index >= 0) {
1357d06d 227 orangefs_bufmap_put(buffer_index);
5db11c21
MM
228 gossip_debug(GOSSIP_FILE_DEBUG,
229 "%s(%pU): PUT buffer_index %d\n",
230 __func__, handle, buffer_index);
231 buffer_index = -1;
232 }
ed42fe05 233 op_release(new_op);
5db11c21
MM
234 return ret;
235}
236
5db11c21
MM
237/*
238 * Common entry point for read/write/readv/writev
239 * This function will dispatch it to either the direct I/O
240 * or buffered I/O path depending on the mount options and/or
241 * augmented/extended metadata attached to the file.
242 * Note: File extended attributes override any mount options.
243 */
c453dcfc 244ssize_t do_readv_writev(enum ORANGEFS_io_type type, struct file *file,
0071ed1e 245 loff_t *offset, struct iov_iter *iter)
5db11c21
MM
246{
247 struct inode *inode = file->f_mapping->host;
8bb8aefd
YL
248 struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
249 struct orangefs_khandle *handle = &orangefs_inode->refn.khandle;
0071ed1e 250 size_t count = iov_iter_count(iter);
dc4067f6
AV
251 ssize_t total_count = 0;
252 ssize_t ret = -EINVAL;
5db11c21
MM
253
254 gossip_debug(GOSSIP_FILE_DEBUG,
255 "%s-BEGIN(%pU): count(%d) after estimate_max_iovecs.\n",
256 __func__,
257 handle,
258 (int)count);
259
8bb8aefd 260 if (type == ORANGEFS_IO_WRITE) {
5db11c21
MM
261 gossip_debug(GOSSIP_FILE_DEBUG,
262 "%s(%pU): proceeding with offset : %llu, "
263 "size %d\n",
264 __func__,
265 handle,
266 llu(*offset),
267 (int)count);
268 }
269
270 if (count == 0) {
271 ret = 0;
272 goto out;
273 }
274
0071ed1e
AV
275 while (iov_iter_count(iter)) {
276 size_t each_count = iov_iter_count(iter);
5db11c21
MM
277 size_t amt_complete;
278
279 /* how much to transfer in this loop iteration */
8bb8aefd
YL
280 if (each_count > orangefs_bufmap_size_query())
281 each_count = orangefs_bufmap_size_query();
5db11c21
MM
282
283 gossip_debug(GOSSIP_FILE_DEBUG,
284 "%s(%pU): size of each_count(%d)\n",
285 __func__,
286 handle,
287 (int)each_count);
288 gossip_debug(GOSSIP_FILE_DEBUG,
289 "%s(%pU): BEFORE wait_for_io: offset is %d\n",
290 __func__,
291 handle,
292 (int)*offset);
293
0071ed1e 294 ret = wait_for_direct_io(type, inode, offset, iter,
3c2fcfcb 295 each_count, 0);
5db11c21
MM
296 gossip_debug(GOSSIP_FILE_DEBUG,
297 "%s(%pU): return from wait_for_io:%d\n",
298 __func__,
299 handle,
300 (int)ret);
301
302 if (ret < 0)
303 goto out;
304
5db11c21
MM
305 *offset += ret;
306 total_count += ret;
307 amt_complete = ret;
308
309 gossip_debug(GOSSIP_FILE_DEBUG,
310 "%s(%pU): AFTER wait_for_io: offset is %d\n",
311 __func__,
312 handle,
313 (int)*offset);
314
315 /*
316 * if we got a short I/O operations,
317 * fall out and return what we got so far
318 */
319 if (amt_complete < each_count)
320 break;
321 } /*end while */
322
6d4c1a30 323out:
5db11c21
MM
324 if (total_count > 0)
325 ret = total_count;
5db11c21 326 if (ret > 0) {
8bb8aefd 327 if (type == ORANGEFS_IO_READ) {
5db11c21
MM
328 file_accessed(file);
329 } else {
a55f2d86 330 file_update_time(file);
5e7f1d43
MB
331 if (*offset > i_size_read(inode))
332 i_size_write(inode, *offset);
5db11c21
MM
333 }
334 }
335
336 gossip_debug(GOSSIP_FILE_DEBUG,
337 "%s(%pU): Value(%d) returned.\n",
338 __func__,
339 handle,
340 (int)ret);
341
342 return ret;
343}
344
c453dcfc
MB
345static ssize_t orangefs_file_read_iter(struct kiocb *iocb,
346 struct iov_iter *iter)
5db11c21 347{
889d5f1b 348 orangefs_stats.reads++;
c453dcfc 349 return generic_file_read_iter(iocb, iter);
5db11c21
MM
350}
351
85ac799c
MB
352static ssize_t orangefs_file_write_iter(struct kiocb *iocb,
353 struct iov_iter *iter)
5db11c21 354{
889d5f1b 355 orangefs_stats.writes++;
85ac799c 356 return generic_file_write_iter(iocb, iter);
5db11c21
MM
357}
358
359/*
360 * Perform a miscellaneous operation on a file.
361 */
8bb8aefd 362static long orangefs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5db11c21
MM
363{
364 int ret = -ENOTTY;
365 __u64 val = 0;
366 unsigned long uval;
367
368 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 369 "orangefs_ioctl: called with cmd %d\n",
5db11c21
MM
370 cmd);
371
372 /*
373 * we understand some general ioctls on files, such as the immutable
374 * and append flags
375 */
376 if (cmd == FS_IOC_GETFLAGS) {
377 val = 0;
8bb8aefd 378 ret = orangefs_inode_getxattr(file_inode(file),
8bb8aefd
YL
379 "user.pvfs2.meta_hint",
380 &val, sizeof(val));
5db11c21
MM
381 if (ret < 0 && ret != -ENODATA)
382 return ret;
383 else if (ret == -ENODATA)
384 val = 0;
385 uval = val;
386 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 387 "orangefs_ioctl: FS_IOC_GETFLAGS: %llu\n",
5db11c21
MM
388 (unsigned long long)uval);
389 return put_user(uval, (int __user *)arg);
390 } else if (cmd == FS_IOC_SETFLAGS) {
391 ret = 0;
392 if (get_user(uval, (int __user *)arg))
393 return -EFAULT;
394 /*
8bb8aefd 395 * ORANGEFS_MIRROR_FL is set internally when the mirroring mode
5db11c21
MM
396 * is turned on for a file. The user is not allowed to turn
397 * on this bit, but the bit is present if the user first gets
398 * the flags and then updates the flags with some new
399 * settings. So, we ignore it in the following edit. bligon.
400 */
8bb8aefd 401 if ((uval & ~ORANGEFS_MIRROR_FL) &
5db11c21 402 (~(FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NOATIME_FL))) {
8bb8aefd 403 gossip_err("orangefs_ioctl: the FS_IOC_SETFLAGS only supports setting one of FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NOATIME_FL\n");
5db11c21
MM
404 return -EINVAL;
405 }
406 val = uval;
407 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 408 "orangefs_ioctl: FS_IOC_SETFLAGS: %llu\n",
5db11c21 409 (unsigned long long)val);
8bb8aefd 410 ret = orangefs_inode_setxattr(file_inode(file),
8bb8aefd
YL
411 "user.pvfs2.meta_hint",
412 &val, sizeof(val), 0);
5db11c21
MM
413 }
414
415 return ret;
416}
417
8bf782f6 418static vm_fault_t orangefs_fault(struct vm_fault *vmf)
a5135eea
MB
419{
420 struct file *file = vmf->vma->vm_file;
8bf782f6 421 int ret;
8b60785c
MB
422 ret = orangefs_inode_getattr(file->f_mapping->host,
423 ORANGEFS_GETATTR_SIZE);
8bf782f6
SJ
424 if (ret == -ESTALE)
425 ret = -EIO;
426 if (ret) {
8b60785c
MB
427 gossip_err("%s: orangefs_inode_getattr failed, "
428 "ret:%d:.\n", __func__, ret);
8bf782f6 429 return VM_FAULT_SIGBUS;
a5135eea
MB
430 }
431 return filemap_fault(vmf);
432}
433
ec62e95a 434static const struct vm_operations_struct orangefs_file_vm_ops = {
a5135eea
MB
435 .fault = orangefs_fault,
436 .map_pages = filemap_map_pages,
437 .page_mkwrite = filemap_page_mkwrite,
438};
439
5db11c21
MM
440/*
441 * Memory map a region of a file.
442 */
8bb8aefd 443static int orangefs_file_mmap(struct file *file, struct vm_area_struct *vma)
5db11c21
MM
444{
445 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 446 "orangefs_file_mmap: called on %s\n",
5db11c21
MM
447 (file ?
448 (char *)file->f_path.dentry->d_name.name :
449 (char *)"Unknown"));
450
451 /* set the sequential readahead hint */
452 vma->vm_flags |= VM_SEQ_READ;
453 vma->vm_flags &= ~VM_RAND_READ;
35390803 454
a5135eea
MB
455 file_accessed(file);
456 vma->vm_ops = &orangefs_file_vm_ops;
457 return 0;
5db11c21
MM
458}
459
460#define mapping_nrpages(idata) ((idata)->nrpages)
461
462/*
463 * Called to notify the module that there are no more references to
464 * this file (i.e. no processes have it open).
465 *
466 * \note Not called when each file is closed.
467 */
8bb8aefd 468static int orangefs_file_release(struct inode *inode, struct file *file)
5db11c21
MM
469{
470 gossip_debug(GOSSIP_FILE_DEBUG,
f66debf1
AV
471 "orangefs_file_release: called on %pD\n",
472 file);
5db11c21 473
5db11c21 474 /*
6eaff8c7 475 * remove all associated inode pages from the page cache and
54804949
MM
476 * readahead cache (if any); this forces an expensive refresh of
477 * data for the next caller of mmap (or 'get_block' accesses)
5db11c21 478 */
d62a9025
AG
479 if (file_inode(file) &&
480 file_inode(file)->i_mapping &&
481 mapping_nrpages(&file_inode(file)->i_data)) {
c51e0129
MB
482 if (orangefs_features & ORANGEFS_FEATURE_READAHEAD) {
483 gossip_debug(GOSSIP_INODE_DEBUG,
484 "calling flush_racache on %pU\n",
485 get_khandle_from_ino(inode));
486 flush_racache(inode);
487 gossip_debug(GOSSIP_INODE_DEBUG,
488 "flush_racache finished\n");
489 }
ed1e1587 490 }
5db11c21
MM
491 return 0;
492}
493
494/*
495 * Push all data for a specific file onto permanent storage.
496 */
8bb8aefd 497static int orangefs_fsync(struct file *file,
84d02150
MM
498 loff_t start,
499 loff_t end,
500 int datasync)
5db11c21 501{
49e55713 502 int ret;
8bb8aefd 503 struct orangefs_inode_s *orangefs_inode =
d62a9025 504 ORANGEFS_I(file_inode(file));
8bb8aefd 505 struct orangefs_kernel_op_s *new_op = NULL;
5db11c21 506
85ac799c
MB
507 ret = filemap_write_and_wait_range(file_inode(file)->i_mapping,
508 start, end);
509 if (ret < 0)
510 return ret;
511
8bb8aefd 512 new_op = op_alloc(ORANGEFS_VFS_OP_FSYNC);
5db11c21
MM
513 if (!new_op)
514 return -ENOMEM;
8bb8aefd 515 new_op->upcall.req.fsync.refn = orangefs_inode->refn;
5db11c21
MM
516
517 ret = service_operation(new_op,
8bb8aefd 518 "orangefs_fsync",
d62a9025 519 get_interruptible_flag(file_inode(file)));
5db11c21
MM
520
521 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 522 "orangefs_fsync got return value of %d\n",
5db11c21
MM
523 ret);
524
525 op_release(new_op);
5db11c21
MM
526 return ret;
527}
528
529/*
530 * Change the file pointer position for an instance of an open file.
531 *
532 * \note If .llseek is overriden, we must acquire lock as described in
533 * Documentation/filesystems/Locking.
534 *
535 * Future upgrade could support SEEK_DATA and SEEK_HOLE but would
536 * require much changes to the FS
537 */
8bb8aefd 538static loff_t orangefs_file_llseek(struct file *file, loff_t offset, int origin)
5db11c21
MM
539{
540 int ret = -EINVAL;
177f8fc4 541 struct inode *inode = file_inode(file);
5db11c21 542
177f8fc4 543 if (origin == SEEK_END) {
5db11c21
MM
544 /*
545 * revalidate the inode's file size.
546 * NOTE: We are only interested in file size here,
547 * so we set mask accordingly.
548 */
8b60785c
MB
549 ret = orangefs_inode_getattr(file->f_mapping->host,
550 ORANGEFS_GETATTR_SIZE);
e2f7f0d7
MB
551 if (ret == -ESTALE)
552 ret = -EIO;
5db11c21
MM
553 if (ret) {
554 gossip_debug(GOSSIP_FILE_DEBUG,
555 "%s:%s:%d calling make bad inode\n",
556 __FILE__,
557 __func__,
558 __LINE__);
5db11c21
MM
559 return ret;
560 }
561 }
562
563 gossip_debug(GOSSIP_FILE_DEBUG,
8bb8aefd 564 "orangefs_file_llseek: offset is %ld | origin is %d"
54804949 565 " | inode size is %lu\n",
5db11c21
MM
566 (long)offset,
567 origin,
177f8fc4 568 (unsigned long)i_size_read(inode));
5db11c21
MM
569
570 return generic_file_llseek(file, offset, origin);
571}
572
573/*
574 * Support local locks (locks that only this kernel knows about)
575 * if Orangefs was mounted -o local_lock.
576 */
8bb8aefd 577static int orangefs_lock(struct file *filp, int cmd, struct file_lock *fl)
5db11c21 578{
f957ae2d 579 int rc = -EINVAL;
5db11c21 580
45063097 581 if (ORANGEFS_SB(file_inode(filp)->i_sb)->flags & ORANGEFS_OPT_LOCAL_LOCK) {
5db11c21
MM
582 if (cmd == F_GETLK) {
583 rc = 0;
584 posix_test_lock(filp, fl);
585 } else {
586 rc = posix_lock_file(filp, fl, NULL);
587 }
588 }
589
590 return rc;
591}
592
85ac799c
MB
593static int orangefs_flush(struct file *file, fl_owner_t id)
594{
595 return vfs_fsync(file, 0);
596}
597
8bb8aefd
YL
598/** ORANGEFS implementation of VFS file operations */
599const struct file_operations orangefs_file_operations = {
600 .llseek = orangefs_file_llseek,
601 .read_iter = orangefs_file_read_iter,
602 .write_iter = orangefs_file_write_iter,
603 .lock = orangefs_lock,
604 .unlocked_ioctl = orangefs_ioctl,
605 .mmap = orangefs_file_mmap,
5db11c21 606 .open = generic_file_open,
85ac799c 607 .flush = orangefs_flush,
8bb8aefd
YL
608 .release = orangefs_file_release,
609 .fsync = orangefs_fsync,
5db11c21 610};
This page took 0.577623 seconds and 4 git commands to generate.