8
8
9
9
#include "fuse_i.h"
10
10
11
+ #include "linux/list.h"
12
+ #include "linux/printk.h"
13
+ #include "linux/spinlock.h"
11
14
#include <linux/pagemap.h>
12
15
#include <linux/slab.h>
13
16
#include <linux/kernel.h>
@@ -1395,6 +1398,149 @@ static void fuse_dio_unlock(struct kiocb *iocb, bool exclusive)
1395
1398
}
1396
1399
}
1397
1400
1401
+ static bool fuse_dlm_locked (struct file * file , loff_t offset , size_t length )
1402
+ {
1403
+ struct inode * inode = file_inode (file );
1404
+ struct fuse_conn * fc = get_fuse_conn (inode );
1405
+ struct fuse_inode * fi = get_fuse_inode (inode );
1406
+ struct dlm_locked_area * area ;
1407
+
1408
+ /* if dlm is not supported by fuse server, don't bother */
1409
+ if (fc -> no_dlm )
1410
+ return true;
1411
+
1412
+ /* check the locked areas for the given offset and length */
1413
+ list_for_each_entry (area , & fi -> dlm_locked_areas , list ) {
1414
+ loff_t current_area_start_offset = area -> offset ;
1415
+ loff_t current_area_end_offset = area -> offset + area -> size ;
1416
+ loff_t lock_end_offset = offset + length ;
1417
+ loff_t lock_start_offset = offset ;
1418
+
1419
+ /* check if the locked areas are completely distinct, then we should continue */
1420
+ if (current_area_end_offset < lock_start_offset
1421
+ || current_area_start_offset > lock_end_offset )
1422
+ continue ;
1423
+
1424
+ /* check if the given offset and length completely overlaps with the current area */
1425
+ if (current_area_start_offset <= lock_start_offset
1426
+ && current_area_end_offset >= lock_end_offset ) {
1427
+ return true;
1428
+ }
1429
+
1430
+ /* lock area has segment after the current area */
1431
+ if (current_area_start_offset < lock_start_offset
1432
+ && current_area_end_offset > lock_start_offset
1433
+ && current_area_end_offset < lock_end_offset ) {
1434
+ offset = current_area_end_offset ;
1435
+ length = lock_end_offset - current_area_end_offset ;
1436
+ /* check all other areas for the part at the end of the locked area */
1437
+ return fuse_dlm_locked (file , offset , length );
1438
+ }
1439
+
1440
+ /* lock area has segment before the current area */
1441
+ if (lock_start_offset < current_area_start_offset
1442
+ && lock_end_offset > current_area_start_offset
1443
+ && lock_end_offset < current_area_end_offset ) {
1444
+ offset = lock_start_offset ;
1445
+ length = current_area_start_offset - lock_start_offset ;
1446
+ /* check all other areas for the rest of the part */
1447
+ return fuse_dlm_locked (file , offset , length );
1448
+ }
1449
+
1450
+ /* If the lock area is larger than the current area, continue, some other areas might match partially
1451
+ * If they don't we return false and the bigger chunk will be locked and merged with the partially matching one anyway.
1452
+ * This is a case the fuse server has to be able to handle.
1453
+ */
1454
+ }
1455
+ return false;
1456
+ }
1457
+
1458
+ /**
1459
+ * check if the given offset and length extends the already locked area or we have to create a new area
1460
+ */
1461
+ static void check_and_add_locked_area (struct fuse_inode * fi , loff_t offset , size_t length ) {
1462
+ struct dlm_locked_area * area ;
1463
+
1464
+ spin_lock (& fi -> lock );
1465
+ /* iterate through the areas */
1466
+ list_for_each_entry (area , & fi -> dlm_locked_areas , list ) {
1467
+ loff_t current_area_start_offset = area -> offset ;
1468
+ loff_t current_area_end_offset = area -> offset + area -> size ;
1469
+ loff_t lock_end_offset = offset + length ;
1470
+ loff_t lock_start_offset = offset ;
1471
+
1472
+ /* if we have overlap, extend the locked area */
1473
+ if (lock_start_offset >= current_area_start_offset && lock_start_offset <= current_area_end_offset ) {
1474
+ area -> offset = MIN (current_area_start_offset , lock_start_offset );
1475
+ area -> size = MAX (current_area_end_offset , lock_end_offset ) - area -> offset ;
1476
+ spin_unlock (& fi -> lock );
1477
+ return ;
1478
+ }
1479
+ }
1480
+
1481
+ /* create a new locked area */
1482
+ area = kmalloc (sizeof (struct dlm_locked_area ), GFP_KERNEL );
1483
+ area -> offset = offset ;
1484
+ area -> size = length ;
1485
+ list_add_tail (& area -> list , & fi -> dlm_locked_areas );
1486
+ spin_unlock (& fi -> lock );
1487
+ }
1488
+
1489
+ /**
1490
+ * request a dlm lock from the fuse server
1491
+ */
1492
+ static void fuse_get_dlm_write_lock (struct file * file , loff_t offset , size_t length )
1493
+ {
1494
+ struct fuse_file * ff = file -> private_data ;
1495
+ struct inode * inode = file_inode (file );
1496
+ struct fuse_conn * fc = get_fuse_conn (inode );
1497
+ struct fuse_inode * fi = get_fuse_inode (inode );
1498
+ struct fuse_mount * fm = ff -> fm ;
1499
+
1500
+ FUSE_ARGS (args );
1501
+ struct fuse_dlm_lock_in inarg ;
1502
+ struct fuse_dlm_lock_out outarg ;
1503
+ int err ;
1504
+
1505
+ /* note that the offset and length don't have to be page aligned here
1506
+ but since we only get here on writeback caching we will send out
1507
+ page aligned requests */
1508
+ offset &= PAGE_MASK ;
1509
+ length = PAGE_ALIGN (offset + length ) - offset ;
1510
+
1511
+ if (fuse_dlm_locked (file , offset , length ))
1512
+ return ; /* we already have this area locked */
1513
+
1514
+ memset (& inarg , 0 , sizeof (inarg ));
1515
+ inarg .fh = ff -> fh ;
1516
+
1517
+ inarg .offset = offset ;
1518
+ inarg .size = length ;
1519
+ inarg .type = FUSE_DLM_LOCK_WRITE ;
1520
+
1521
+ args .opcode = FUSE_DLM_LOCK ;
1522
+ args .nodeid = get_node_id (inode );
1523
+ args .in_numargs = 1 ;
1524
+ args .in_args [0 ].size = sizeof (inarg );
1525
+ args .in_args [0 ].value = & inarg ;
1526
+ args .out_numargs = 1 ;
1527
+ args .out_args [0 ].size = sizeof (outarg );
1528
+ args .out_args [0 ].value = & outarg ;
1529
+ err = fuse_simple_request (fm , & args );
1530
+ if (err == - ENOSYS ) {
1531
+ /* fuse server told us it does not support dlm, save the info */
1532
+ fc -> no_dlm = 1 ;
1533
+ }
1534
+
1535
+ if (err )
1536
+ return ;
1537
+
1538
+ /* add the lock to the list of locked areas */
1539
+ if (outarg .locksize ) {
1540
+ check_and_add_locked_area (fi , offset , outarg .locksize );
1541
+ }
1542
+ }
1543
+
1398
1544
static ssize_t fuse_cache_write_iter (struct kiocb * iocb , struct iov_iter * from )
1399
1545
{
1400
1546
struct file * file = iocb -> ki_filp ;
@@ -1403,6 +1549,8 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
1403
1549
struct inode * inode = mapping -> host ;
1404
1550
ssize_t err ;
1405
1551
struct fuse_conn * fc = get_fuse_conn (inode );
1552
+ loff_t pos = iocb -> ki_pos ;
1553
+ size_t length = iov_iter_count (from );
1406
1554
1407
1555
if (fc -> writeback_cache ) {
1408
1556
/* Update size (EOF optimization) and mode (SUID clearing) */
@@ -1417,6 +1565,11 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
1417
1565
goto writethrough ;
1418
1566
}
1419
1567
1568
+ /* if we have dlm support acquire the lock for the area
1569
+ * we are writing into */
1570
+ if (!fc -> no_dlm )
1571
+ fuse_get_dlm_write_lock (file , pos , length );
1572
+
1420
1573
return generic_file_write_iter (iocb , from );
1421
1574
}
1422
1575
@@ -3325,6 +3478,7 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags)
3325
3478
3326
3479
INIT_LIST_HEAD (& fi -> write_files );
3327
3480
INIT_LIST_HEAD (& fi -> queued_writes );
3481
+ INIT_LIST_HEAD (& fi -> dlm_locked_areas );
3328
3482
fi -> writectr = 0 ;
3329
3483
fi -> iocachectr = 0 ;
3330
3484
init_waitqueue_head (& fi -> page_waitq );
0 commit comments