diff options
author | Chris Mason <chris.mason@oracle.com> | 2007-04-17 13:26:50 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@hera.kernel.org> | 2007-04-17 13:26:50 -0400 |
commit | b18c6685810af8e6763760711aece31ccc7a8ea8 (patch) | |
tree | db7220ed6fb418fbdc069ac422fc8b1c1598e92f /fs/btrfs/super.c | |
parent | 6567e837df07e43bffc08ac40858af8133a007bf (diff) |
Btrfs: progress on file_write
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/super.c')
-rw-r--r-- | fs/btrfs/super.c | 284 |
1 files changed, 269 insertions, 15 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index ec689992fdf4..6a56416147e6 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -1089,7 +1089,6 @@ static int btrfs_get_block_lock(struct inode *inode, sector_t iblock, | |||
1089 | 1089 | ||
1090 | extent_start = btrfs_disk_key_offset(&leaf->items[path->slots[0]].key); | 1090 | extent_start = btrfs_disk_key_offset(&leaf->items[path->slots[0]].key); |
1091 | extent_start = extent_start >> inode->i_blkbits; | 1091 | extent_start = extent_start >> inode->i_blkbits; |
1092 | extent_start += btrfs_file_extent_offset(item); | ||
1093 | extent_end = extent_start + btrfs_file_extent_num_blocks(item); | 1092 | extent_end = extent_start + btrfs_file_extent_num_blocks(item); |
1094 | if (iblock >= extent_start && iblock < extent_end) { | 1093 | if (iblock >= extent_start && iblock < extent_end) { |
1095 | err = 0; | 1094 | err = 0; |
@@ -1103,6 +1102,7 @@ allocate: | |||
1103 | err = 0; | 1102 | err = 0; |
1104 | goto out; | 1103 | goto out; |
1105 | } | 1104 | } |
1105 | #if 0 | ||
1106 | ret = btrfs_alloc_file_extent(trans, root, objectid, | 1106 | ret = btrfs_alloc_file_extent(trans, root, objectid, |
1107 | iblock << inode->i_blkbits, | 1107 | iblock << inode->i_blkbits, |
1108 | 1, extent_end, &blocknr); | 1108 | 1, extent_end, &blocknr); |
@@ -1115,9 +1115,11 @@ allocate: | |||
1115 | map_bh(result, inode->i_sb, blocknr); | 1115 | map_bh(result, inode->i_sb, blocknr); |
1116 | 1116 | ||
1117 | btrfs_map_bh_to_logical(root, result, blocknr); | 1117 | btrfs_map_bh_to_logical(root, result, blocknr); |
1118 | #endif | ||
1118 | out: | 1119 | out: |
1119 | btrfs_release_path(root, path); | 1120 | btrfs_release_path(root, path); |
1120 | btrfs_free_path(path); | 1121 | btrfs_free_path(path); |
1122 | printk("mapping iblock %lu to %lu\n", iblock, result->b_blocknr); | ||
1121 | if (trans) | 1123 | if (trans) |
1122 | btrfs_end_transaction(trans, root); | 1124 | btrfs_end_transaction(trans, root); |
1123 | return err; | 1125 | return err; |
@@ -1273,8 +1275,244 @@ failed: | |||
1273 | return err; | 1275 | return err; |
1274 | } | 1276 | } |
1275 | 1277 | ||
1276 | static int prepare_pages(struct btrfs_trans_handle *trans, | 1278 | static int drop_csums(struct btrfs_trans_handle *trans, |
1277 | struct btrfs_root *root, | 1279 | struct btrfs_root *root, |
1280 | struct inode *inode, | ||
1281 | u64 start, u64 end) | ||
1282 | { | ||
1283 | struct btrfs_path *path; | ||
1284 | struct btrfs_leaf *leaf; | ||
1285 | struct btrfs_key key; | ||
1286 | int slot; | ||
1287 | struct btrfs_csum_item *item; | ||
1288 | char *old_block = NULL; | ||
1289 | u64 cur = start; | ||
1290 | u64 found_end; | ||
1291 | u64 num_csums; | ||
1292 | u64 item_size; | ||
1293 | int ret; | ||
1294 | |||
1295 | path = btrfs_alloc_path(); | ||
1296 | if (!path) | ||
1297 | return -ENOMEM; | ||
1298 | while(cur < end) { | ||
1299 | item = btrfs_lookup_csum(trans, root, path, | ||
1300 | inode->i_ino, cur, 1); | ||
1301 | if (IS_ERR(item)) { | ||
1302 | cur += root->blocksize; | ||
1303 | continue; | ||
1304 | } | ||
1305 | leaf = btrfs_buffer_leaf(path->nodes[0]); | ||
1306 | slot = path->slots[0]; | ||
1307 | btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key); | ||
1308 | item_size = btrfs_item_size(leaf->items + slot); | ||
1309 | num_csums = item_size / sizeof(struct btrfs_csum_item); | ||
1310 | found_end = key.offset + (num_csums << inode->i_blkbits); | ||
1311 | cur = found_end; | ||
1312 | |||
1313 | if (found_end > end) { | ||
1314 | char *src; | ||
1315 | old_block = kmalloc(root->blocksize, GFP_NOFS); | ||
1316 | src = btrfs_item_ptr(leaf, slot, char); | ||
1317 | memcpy(old_block, src, item_size); | ||
1318 | } | ||
1319 | if (key.offset < start) { | ||
1320 | u64 new_size = (start - key.offset) >> | ||
1321 | inode->i_blkbits; | ||
1322 | new_size *= sizeof(struct btrfs_csum_item); | ||
1323 | ret = btrfs_truncate_item(trans, root, path, new_size); | ||
1324 | BUG_ON(ret); | ||
1325 | } else { | ||
1326 | btrfs_del_item(trans, root, path); | ||
1327 | } | ||
1328 | btrfs_release_path(root, path); | ||
1329 | if (found_end > end) { | ||
1330 | char *dst; | ||
1331 | int i; | ||
1332 | int new_size; | ||
1333 | |||
1334 | num_csums = (found_end - end) >> inode->i_blkbits; | ||
1335 | new_size = num_csums * sizeof(struct btrfs_csum_item); | ||
1336 | key.offset = end; | ||
1337 | ret = btrfs_insert_empty_item(trans, root, path, | ||
1338 | &key, new_size); | ||
1339 | BUG_ON(ret); | ||
1340 | dst = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), | ||
1341 | path->slots[0], char); | ||
1342 | memcpy(dst, old_block + item_size - new_size, | ||
1343 | new_size); | ||
1344 | item = (struct btrfs_csum_item *)dst; | ||
1345 | for (i = 0; i < num_csums; i++) { | ||
1346 | btrfs_set_csum_extent_offset(item, end); | ||
1347 | item++; | ||
1348 | } | ||
1349 | mark_buffer_dirty(path->nodes[0]); | ||
1350 | kfree(old_block); | ||
1351 | break; | ||
1352 | } | ||
1353 | } | ||
1354 | btrfs_free_path(path); | ||
1355 | return 0; | ||
1356 | } | ||
1357 | |||
1358 | static int drop_extents(struct btrfs_trans_handle *trans, | ||
1359 | struct btrfs_root *root, | ||
1360 | struct inode *inode, | ||
1361 | u64 start, u64 end) | ||
1362 | { | ||
1363 | int ret; | ||
1364 | struct btrfs_key key; | ||
1365 | struct btrfs_leaf *leaf; | ||
1366 | int slot; | ||
1367 | struct btrfs_file_extent_item *extent; | ||
1368 | u64 extent_end; | ||
1369 | int keep; | ||
1370 | struct btrfs_file_extent_item old; | ||
1371 | struct btrfs_path *path; | ||
1372 | u64 search_start = start; | ||
1373 | int bookend; | ||
1374 | |||
1375 | path = btrfs_alloc_path(); | ||
1376 | if (!path) | ||
1377 | return -ENOMEM; | ||
1378 | search_again: | ||
1379 | printk("drop extent inode %lu start %Lu end %Lu\n", inode->i_ino, start, end); | ||
1380 | ret = btrfs_lookup_file_extent(trans, root, path, inode->i_ino, | ||
1381 | search_start, -1); | ||
1382 | if (ret != 0) { | ||
1383 | printk("lookup failed\n"); | ||
1384 | goto out; | ||
1385 | } | ||
1386 | while(1) { | ||
1387 | keep = 0; | ||
1388 | bookend = 0; | ||
1389 | leaf = btrfs_buffer_leaf(path->nodes[0]); | ||
1390 | slot = path->slots[0]; | ||
1391 | btrfs_disk_key_to_cpu(&key, &leaf->items[slot].key); | ||
1392 | |||
1393 | printk("found key %Lu %Lu %u\n", key.objectid, key.offset, key.flags); | ||
1394 | |||
1395 | extent = btrfs_item_ptr(leaf, slot, | ||
1396 | struct btrfs_file_extent_item); | ||
1397 | extent_end = key.offset + | ||
1398 | (btrfs_file_extent_num_blocks(extent) << | ||
1399 | inode->i_blkbits); | ||
1400 | printk("extent end is %Lu\n", extent_end); | ||
1401 | if (key.offset >= end || key.objectid != inode->i_ino) { | ||
1402 | ret = 0; | ||
1403 | goto out; | ||
1404 | } | ||
1405 | if (btrfs_key_type(&key) != BTRFS_EXTENT_DATA_KEY) | ||
1406 | goto next_leaf; | ||
1407 | |||
1408 | if (end < extent_end && end >= key.offset) { | ||
1409 | memcpy(&old, extent, sizeof(old)); | ||
1410 | ret = btrfs_inc_extent_ref(trans, root, | ||
1411 | btrfs_file_extent_disk_blocknr(&old), | ||
1412 | btrfs_file_extent_disk_num_blocks(&old)); | ||
1413 | BUG_ON(ret); | ||
1414 | bookend = 1; | ||
1415 | } | ||
1416 | |||
1417 | if (start > key.offset) { | ||
1418 | u64 new_num; | ||
1419 | /* truncate existing extent */ | ||
1420 | keep = 1; | ||
1421 | WARN_ON(start & (root->blocksize - 1)); | ||
1422 | new_num = (start - key.offset) >> inode->i_blkbits; | ||
1423 | printk("truncating existing extent, was %Lu ", btrfs_file_extent_num_blocks(extent)); | ||
1424 | btrfs_set_file_extent_num_blocks(extent, new_num); | ||
1425 | printk("now %Lu\n", btrfs_file_extent_num_blocks(extent)); | ||
1426 | |||
1427 | mark_buffer_dirty(path->nodes[0]); | ||
1428 | } | ||
1429 | if (!keep) { | ||
1430 | u64 disk_blocknr; | ||
1431 | u64 disk_num_blocks; | ||
1432 | printk("del old\n"); | ||
1433 | disk_blocknr = btrfs_file_extent_disk_blocknr(extent); | ||
1434 | disk_num_blocks = | ||
1435 | btrfs_file_extent_disk_num_blocks(extent); | ||
1436 | search_start = key.offset + | ||
1437 | (btrfs_file_extent_num_blocks(extent) << | ||
1438 | inode->i_blkbits); | ||
1439 | ret = btrfs_del_item(trans, root, path); | ||
1440 | BUG_ON(ret); | ||
1441 | btrfs_release_path(root, path); | ||
1442 | |||
1443 | ret = btrfs_free_extent(trans, root, disk_blocknr, | ||
1444 | disk_num_blocks, 0); | ||
1445 | |||
1446 | BUG_ON(ret); | ||
1447 | if (!bookend && search_start >= end) { | ||
1448 | ret = 0; | ||
1449 | goto out; | ||
1450 | } | ||
1451 | if (!bookend) | ||
1452 | goto search_again; | ||
1453 | } | ||
1454 | if (bookend) { | ||
1455 | /* create bookend */ | ||
1456 | struct btrfs_key ins; | ||
1457 | printk("bookend! extent end %Lu\n", extent_end); | ||
1458 | ins.objectid = inode->i_ino; | ||
1459 | ins.offset = end; | ||
1460 | ins.flags = 0; | ||
1461 | btrfs_set_key_type(&ins, BTRFS_EXTENT_DATA_KEY); | ||
1462 | |||
1463 | btrfs_release_path(root, path); | ||
1464 | ret = drop_csums(trans, root, inode, start, end); | ||
1465 | BUG_ON(ret); | ||
1466 | ret = btrfs_insert_empty_item(trans, root, path, &ins, | ||
1467 | sizeof(*extent)); | ||
1468 | BUG_ON(ret); | ||
1469 | extent = btrfs_item_ptr( | ||
1470 | btrfs_buffer_leaf(path->nodes[0]), | ||
1471 | path->slots[0], | ||
1472 | struct btrfs_file_extent_item); | ||
1473 | btrfs_set_file_extent_disk_blocknr(extent, | ||
1474 | btrfs_file_extent_disk_blocknr(&old)); | ||
1475 | btrfs_set_file_extent_disk_num_blocks(extent, | ||
1476 | btrfs_file_extent_disk_num_blocks(&old)); | ||
1477 | |||
1478 | btrfs_set_file_extent_offset(extent, | ||
1479 | btrfs_file_extent_offset(&old) + | ||
1480 | ((end - key.offset) >> inode->i_blkbits)); | ||
1481 | WARN_ON(btrfs_file_extent_num_blocks(&old) < | ||
1482 | (end - key.offset) >> inode->i_blkbits); | ||
1483 | btrfs_set_file_extent_num_blocks(extent, | ||
1484 | btrfs_file_extent_num_blocks(&old) - | ||
1485 | ((end - key.offset) >> inode->i_blkbits)); | ||
1486 | |||
1487 | btrfs_set_file_extent_generation(extent, | ||
1488 | btrfs_file_extent_generation(&old)); | ||
1489 | printk("new bookend at offset %Lu, file_extent_offset %Lu, file_extent_num_blocks %Lu\n", end, btrfs_file_extent_offset(extent), btrfs_file_extent_num_blocks(extent)); | ||
1490 | btrfs_mark_buffer_dirty(path->nodes[0]); | ||
1491 | ret = 0; | ||
1492 | goto out_nocsum; | ||
1493 | } | ||
1494 | next_leaf: | ||
1495 | if (slot >= btrfs_header_nritems(&leaf->header) - 1) { | ||
1496 | ret = btrfs_next_leaf(root, path); | ||
1497 | if (ret) { | ||
1498 | ret = 0; | ||
1499 | goto out; | ||
1500 | } | ||
1501 | } else { | ||
1502 | path->slots[0]++; | ||
1503 | } | ||
1504 | } | ||
1505 | |||
1506 | out: | ||
1507 | ret = drop_csums(trans, root, inode, start, end); | ||
1508 | BUG_ON(ret); | ||
1509 | |||
1510 | out_nocsum: | ||
1511 | btrfs_free_path(path); | ||
1512 | return ret; | ||
1513 | } | ||
1514 | |||
1515 | static int prepare_pages(struct btrfs_root *root, | ||
1278 | struct file *file, | 1516 | struct file *file, |
1279 | struct page **pages, | 1517 | struct page **pages, |
1280 | size_t num_pages, | 1518 | size_t num_pages, |
@@ -1289,7 +1527,6 @@ static int prepare_pages(struct btrfs_trans_handle *trans, | |||
1289 | struct inode *inode = file->f_path.dentry->d_inode; | 1527 | struct inode *inode = file->f_path.dentry->d_inode; |
1290 | int offset; | 1528 | int offset; |
1291 | int err = 0; | 1529 | int err = 0; |
1292 | int ret; | ||
1293 | int this_write; | 1530 | int this_write; |
1294 | struct buffer_head *bh; | 1531 | struct buffer_head *bh; |
1295 | struct buffer_head *head; | 1532 | struct buffer_head *head; |
@@ -1305,18 +1542,21 @@ static int prepare_pages(struct btrfs_trans_handle *trans, | |||
1305 | } | 1542 | } |
1306 | offset = pos & (PAGE_CACHE_SIZE -1); | 1543 | offset = pos & (PAGE_CACHE_SIZE -1); |
1307 | this_write = min(PAGE_CACHE_SIZE - offset, write_bytes); | 1544 | this_write = min(PAGE_CACHE_SIZE - offset, write_bytes); |
1308 | if (!PageUptodate(pages[i]) && | 1545 | #if 0 |
1309 | (pages[i]->index == first_index || | 1546 | if ((pages[i]->index == first_index || |
1310 | pages[i]->index == last_index) && pos < isize) { | 1547 | pages[i]->index == last_index) && pos < isize && |
1548 | !PageUptodate(pages[i])) { | ||
1311 | ret = mpage_readpage(pages[i], btrfs_get_block); | 1549 | ret = mpage_readpage(pages[i], btrfs_get_block); |
1312 | BUG_ON(ret); | 1550 | BUG_ON(ret); |
1313 | lock_page(pages[i]); | 1551 | lock_page(pages[i]); |
1314 | } | 1552 | } |
1553 | #endif | ||
1315 | create_empty_buffers(pages[i], root->fs_info->sb->s_blocksize, | 1554 | create_empty_buffers(pages[i], root->fs_info->sb->s_blocksize, |
1316 | (1 << BH_Uptodate)); | 1555 | (1 << BH_Uptodate)); |
1317 | head = page_buffers(pages[i]); | 1556 | head = page_buffers(pages[i]); |
1318 | bh = head; | 1557 | bh = head; |
1319 | do { | 1558 | do { |
1559 | printk("mapping page %lu to block %Lu\n", pages[i]->index, alloc_extent_start); | ||
1320 | err = btrfs_map_bh_to_logical(root, bh, | 1560 | err = btrfs_map_bh_to_logical(root, bh, |
1321 | alloc_extent_start); | 1561 | alloc_extent_start); |
1322 | BUG_ON(err); | 1562 | BUG_ON(err); |
@@ -1351,7 +1591,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
1351 | int ret = 0; | 1591 | int ret = 0; |
1352 | struct inode *inode = file->f_path.dentry->d_inode; | 1592 | struct inode *inode = file->f_path.dentry->d_inode; |
1353 | struct btrfs_root *root = BTRFS_I(inode)->root; | 1593 | struct btrfs_root *root = BTRFS_I(inode)->root; |
1354 | struct page *pages[1]; | 1594 | struct page *pages[8]; |
1355 | unsigned long first_index; | 1595 | unsigned long first_index; |
1356 | unsigned long last_index; | 1596 | unsigned long last_index; |
1357 | u64 start_pos; | 1597 | u64 start_pos; |
@@ -1359,6 +1599,7 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
1359 | u64 alloc_extent_start; | 1599 | u64 alloc_extent_start; |
1360 | u64 orig_extent_start; | 1600 | u64 orig_extent_start; |
1361 | struct btrfs_trans_handle *trans; | 1601 | struct btrfs_trans_handle *trans; |
1602 | struct btrfs_key ins; | ||
1362 | 1603 | ||
1363 | if (file->f_flags & O_DIRECT) | 1604 | if (file->f_flags & O_DIRECT) |
1364 | return -EINVAL; | 1605 | return -EINVAL; |
@@ -1390,16 +1631,24 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
1390 | trans = btrfs_start_transaction(root, 1); | 1631 | trans = btrfs_start_transaction(root, 1); |
1391 | if (!trans) { | 1632 | if (!trans) { |
1392 | err = -ENOMEM; | 1633 | err = -ENOMEM; |
1634 | mutex_unlock(&root->fs_info->fs_mutex); | ||
1393 | goto out_unlock; | 1635 | goto out_unlock; |
1394 | } | 1636 | } |
1395 | ret = btrfs_alloc_file_extent(trans, root, inode->i_ino, | 1637 | if (start_pos < inode->i_size) { |
1396 | start_pos, num_blocks, 1, | 1638 | ret = drop_extents(trans, root, inode, |
1397 | &alloc_extent_start); | 1639 | start_pos, |
1398 | BUG_ON(ret); | 1640 | (pos + count + root->blocksize -1) & |
1399 | 1641 | ~(root->blocksize - 1)); | |
1642 | } | ||
1400 | orig_extent_start = start_pos; | 1643 | orig_extent_start = start_pos; |
1401 | ret = btrfs_end_transaction(trans, root); | 1644 | ret = btrfs_alloc_extent(trans, root, num_blocks, 1, |
1645 | (u64)-1, &ins); | ||
1646 | BUG_ON(ret); | ||
1647 | ret = btrfs_insert_file_extent(trans, root, inode->i_ino, | ||
1648 | start_pos, ins.objectid, ins.offset); | ||
1402 | BUG_ON(ret); | 1649 | BUG_ON(ret); |
1650 | alloc_extent_start = ins.objectid; | ||
1651 | ret = btrfs_end_transaction(trans, root); | ||
1403 | mutex_unlock(&root->fs_info->fs_mutex); | 1652 | mutex_unlock(&root->fs_info->fs_mutex); |
1404 | 1653 | ||
1405 | while(count > 0) { | 1654 | while(count > 0) { |
@@ -1407,16 +1656,21 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
1407 | size_t write_bytes = min(count, PAGE_CACHE_SIZE - offset); | 1656 | size_t write_bytes = min(count, PAGE_CACHE_SIZE - offset); |
1408 | size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >> | 1657 | size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >> |
1409 | PAGE_CACHE_SHIFT; | 1658 | PAGE_CACHE_SHIFT; |
1410 | ret = prepare_pages(NULL, root, file, pages, num_pages, | 1659 | printk("num_pages is %lu\n", num_pages); |
1660 | |||
1661 | memset(pages, 0, sizeof(pages)); | ||
1662 | ret = prepare_pages(root, file, pages, num_pages, | ||
1411 | pos, first_index, last_index, | 1663 | pos, first_index, last_index, |
1412 | write_bytes, alloc_extent_start); | 1664 | write_bytes, alloc_extent_start); |
1413 | BUG_ON(ret); | 1665 | BUG_ON(ret); |
1666 | |||
1414 | /* FIXME blocks != pagesize */ | 1667 | /* FIXME blocks != pagesize */ |
1415 | alloc_extent_start += num_pages; | 1668 | alloc_extent_start += num_pages; |
1416 | ret = btrfs_copy_from_user(pos, num_pages, | 1669 | ret = btrfs_copy_from_user(pos, num_pages, |
1417 | write_bytes, pages, buf); | 1670 | write_bytes, pages, buf); |
1418 | BUG_ON(ret); | 1671 | BUG_ON(ret); |
1419 | 1672 | ||
1673 | printk("2num_pages is %lu\n", num_pages); | ||
1420 | ret = dirty_and_release_pages(NULL, root, file, pages, | 1674 | ret = dirty_and_release_pages(NULL, root, file, pages, |
1421 | num_pages, orig_extent_start, | 1675 | num_pages, orig_extent_start, |
1422 | pos, write_bytes); | 1676 | pos, write_bytes); |