diff options
Diffstat (limited to 'fs/ceph/addr.c')
-rw-r--r-- | fs/ceph/addr.c | 148 |
1 files changed, 146 insertions, 2 deletions
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 13413d7440d6..70a3b441261b 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c | |||
@@ -1313,6 +1313,19 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
1313 | size_t len; | 1313 | size_t len; |
1314 | int want, got, ret; | 1314 | int want, got, ret; |
1315 | 1315 | ||
1316 | if (ci->i_inline_version != CEPH_INLINE_NONE) { | ||
1317 | struct page *locked_page = NULL; | ||
1318 | if (off == 0) { | ||
1319 | lock_page(page); | ||
1320 | locked_page = page; | ||
1321 | } | ||
1322 | ret = ceph_uninline_data(vma->vm_file, locked_page); | ||
1323 | if (locked_page) | ||
1324 | unlock_page(locked_page); | ||
1325 | if (ret < 0) | ||
1326 | return VM_FAULT_SIGBUS; | ||
1327 | } | ||
1328 | |||
1316 | if (off + PAGE_CACHE_SIZE <= size) | 1329 | if (off + PAGE_CACHE_SIZE <= size) |
1317 | len = PAGE_CACHE_SIZE; | 1330 | len = PAGE_CACHE_SIZE; |
1318 | else | 1331 | else |
@@ -1361,11 +1374,13 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
1361 | ret = VM_FAULT_SIGBUS; | 1374 | ret = VM_FAULT_SIGBUS; |
1362 | } | 1375 | } |
1363 | out: | 1376 | out: |
1364 | if (ret != VM_FAULT_LOCKED) { | 1377 | if (ret != VM_FAULT_LOCKED) |
1365 | unlock_page(page); | 1378 | unlock_page(page); |
1366 | } else { | 1379 | if (ret == VM_FAULT_LOCKED || |
1380 | ci->i_inline_version != CEPH_INLINE_NONE) { | ||
1367 | int dirty; | 1381 | int dirty; |
1368 | spin_lock(&ci->i_ceph_lock); | 1382 | spin_lock(&ci->i_ceph_lock); |
1383 | ci->i_inline_version = CEPH_INLINE_NONE; | ||
1369 | dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); | 1384 | dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); |
1370 | spin_unlock(&ci->i_ceph_lock); | 1385 | spin_unlock(&ci->i_ceph_lock); |
1371 | if (dirty) | 1386 | if (dirty) |
@@ -1422,6 +1437,135 @@ void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, | |||
1422 | } | 1437 | } |
1423 | } | 1438 | } |
1424 | 1439 | ||
1440 | int ceph_uninline_data(struct file *filp, struct page *locked_page) | ||
1441 | { | ||
1442 | struct inode *inode = file_inode(filp); | ||
1443 | struct ceph_inode_info *ci = ceph_inode(inode); | ||
1444 | struct ceph_fs_client *fsc = ceph_inode_to_client(inode); | ||
1445 | struct ceph_osd_request *req; | ||
1446 | struct page *page = NULL; | ||
1447 | u64 len, inline_version; | ||
1448 | int err = 0; | ||
1449 | bool from_pagecache = false; | ||
1450 | |||
1451 | spin_lock(&ci->i_ceph_lock); | ||
1452 | inline_version = ci->i_inline_version; | ||
1453 | spin_unlock(&ci->i_ceph_lock); | ||
1454 | |||
1455 | dout("uninline_data %p %llx.%llx inline_version %llu\n", | ||
1456 | inode, ceph_vinop(inode), inline_version); | ||
1457 | |||
1458 | if (inline_version == 1 || /* initial version, no data */ | ||
1459 | inline_version == CEPH_INLINE_NONE) | ||
1460 | goto out; | ||
1461 | |||
1462 | if (locked_page) { | ||
1463 | page = locked_page; | ||
1464 | WARN_ON(!PageUptodate(page)); | ||
1465 | } else if (ceph_caps_issued(ci) & | ||
1466 | (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) { | ||
1467 | page = find_get_page(inode->i_mapping, 0); | ||
1468 | if (page) { | ||
1469 | if (PageUptodate(page)) { | ||
1470 | from_pagecache = true; | ||
1471 | lock_page(page); | ||
1472 | } else { | ||
1473 | page_cache_release(page); | ||
1474 | page = NULL; | ||
1475 | } | ||
1476 | } | ||
1477 | } | ||
1478 | |||
1479 | if (page) { | ||
1480 | len = i_size_read(inode); | ||
1481 | if (len > PAGE_CACHE_SIZE) | ||
1482 | len = PAGE_CACHE_SIZE; | ||
1483 | } else { | ||
1484 | page = __page_cache_alloc(GFP_NOFS); | ||
1485 | if (!page) { | ||
1486 | err = -ENOMEM; | ||
1487 | goto out; | ||
1488 | } | ||
1489 | err = __ceph_do_getattr(inode, page, | ||
1490 | CEPH_STAT_CAP_INLINE_DATA, true); | ||
1491 | if (err < 0) { | ||
1492 | /* no inline data */ | ||
1493 | if (err == -ENODATA) | ||
1494 | err = 0; | ||
1495 | goto out; | ||
1496 | } | ||
1497 | len = err; | ||
1498 | } | ||
1499 | |||
1500 | req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, | ||
1501 | ceph_vino(inode), 0, &len, 0, 1, | ||
1502 | CEPH_OSD_OP_CREATE, | ||
1503 | CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, | ||
1504 | ci->i_snap_realm->cached_context, | ||
1505 | 0, 0, false); | ||
1506 | if (IS_ERR(req)) { | ||
1507 | err = PTR_ERR(req); | ||
1508 | goto out; | ||
1509 | } | ||
1510 | |||
1511 | ceph_osdc_build_request(req, 0, NULL, CEPH_NOSNAP, &inode->i_mtime); | ||
1512 | err = ceph_osdc_start_request(&fsc->client->osdc, req, false); | ||
1513 | if (!err) | ||
1514 | err = ceph_osdc_wait_request(&fsc->client->osdc, req); | ||
1515 | ceph_osdc_put_request(req); | ||
1516 | if (err < 0) | ||
1517 | goto out; | ||
1518 | |||
1519 | req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, | ||
1520 | ceph_vino(inode), 0, &len, 1, 3, | ||
1521 | CEPH_OSD_OP_WRITE, | ||
1522 | CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, | ||
1523 | ci->i_snap_realm->cached_context, | ||
1524 | ci->i_truncate_seq, ci->i_truncate_size, | ||
1525 | false); | ||
1526 | if (IS_ERR(req)) { | ||
1527 | err = PTR_ERR(req); | ||
1528 | goto out; | ||
1529 | } | ||
1530 | |||
1531 | osd_req_op_extent_osd_data_pages(req, 1, &page, len, 0, false, false); | ||
1532 | |||
1533 | err = osd_req_op_xattr_init(req, 0, CEPH_OSD_OP_CMPXATTR, | ||
1534 | "inline_version", &inline_version, | ||
1535 | sizeof(inline_version), | ||
1536 | CEPH_OSD_CMPXATTR_OP_GT, | ||
1537 | CEPH_OSD_CMPXATTR_MODE_U64); | ||
1538 | if (err) | ||
1539 | goto out_put; | ||
1540 | |||
1541 | err = osd_req_op_xattr_init(req, 2, CEPH_OSD_OP_SETXATTR, | ||
1542 | "inline_version", &inline_version, | ||
1543 | sizeof(inline_version), 0, 0); | ||
1544 | if (err) | ||
1545 | goto out_put; | ||
1546 | |||
1547 | ceph_osdc_build_request(req, 0, NULL, CEPH_NOSNAP, &inode->i_mtime); | ||
1548 | err = ceph_osdc_start_request(&fsc->client->osdc, req, false); | ||
1549 | if (!err) | ||
1550 | err = ceph_osdc_wait_request(&fsc->client->osdc, req); | ||
1551 | out_put: | ||
1552 | ceph_osdc_put_request(req); | ||
1553 | if (err == -ECANCELED) | ||
1554 | err = 0; | ||
1555 | out: | ||
1556 | if (page && page != locked_page) { | ||
1557 | if (from_pagecache) { | ||
1558 | unlock_page(page); | ||
1559 | page_cache_release(page); | ||
1560 | } else | ||
1561 | __free_pages(page, 0); | ||
1562 | } | ||
1563 | |||
1564 | dout("uninline_data %p %llx.%llx inline_version %llu = %d\n", | ||
1565 | inode, ceph_vinop(inode), inline_version, err); | ||
1566 | return err; | ||
1567 | } | ||
1568 | |||
1425 | static struct vm_operations_struct ceph_vmops = { | 1569 | static struct vm_operations_struct ceph_vmops = { |
1426 | .fault = ceph_filemap_fault, | 1570 | .fault = ceph_filemap_fault, |
1427 | .page_mkwrite = ceph_page_mkwrite, | 1571 | .page_mkwrite = ceph_page_mkwrite, |