diff options
author | David Howells <dhowells@redhat.com> | 2018-04-06 09:17:25 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-04-09 16:54:48 -0400 |
commit | 63a4681ff39cb63314b8ff41319e70fb0e606ed2 (patch) | |
tree | 0b60198db6c9a21efd1f1dfda2a292ad63df38e8 | |
parent | 0031763698d197d779fcafe5685f6e7818b70d32 (diff) |
afs: Locally edit directory data for mkdir/create/unlink/...
Locally edit the contents of an AFS directory upon a successful inode
operation that modifies that directory (such as mkdir, create and unlink)
so that we can avoid the current practice of re-downloading the directory
after each change.
This is viable provided that the directory version number we get back from
the modifying RPC op is exactly incremented by 1 from what we had
previously. The data in the directory contents is in a defined format that
we have to parse locally to perform lookups and readdir, so modifying isn't
a problem.
If the edit fails, we just clear the VALID flag on the directory and it
will be reloaded next time it is needed.
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | fs/afs/Makefile | 1 | ||||
-rw-r--r-- | fs/afs/dir.c | 83 | ||||
-rw-r--r-- | fs/afs/dir_edit.c | 505 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 35 | ||||
-rw-r--r-- | fs/afs/inode.c | 7 | ||||
-rw-r--r-- | fs/afs/internal.h | 19 | ||||
-rw-r--r-- | fs/afs/proc.c | 4 | ||||
-rw-r--r-- | include/trace/events/afs.h | 90 |
8 files changed, 715 insertions, 29 deletions
diff --git a/fs/afs/Makefile b/fs/afs/Makefile index 6a055423c192..532acae25453 100644 --- a/fs/afs/Makefile +++ b/fs/afs/Makefile | |||
@@ -12,6 +12,7 @@ kafs-objs := \ | |||
12 | cell.o \ | 12 | cell.o \ |
13 | cmservice.o \ | 13 | cmservice.o \ |
14 | dir.o \ | 14 | dir.o \ |
15 | dir_edit.o \ | ||
15 | dynroot.o \ | 16 | dynroot.o \ |
16 | file.o \ | 17 | file.o \ |
17 | flock.o \ | 18 | flock.o \ |
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index f078ae63d870..43bb3b23a879 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -130,17 +130,26 @@ static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page, | |||
130 | qty /= sizeof(union afs_xdr_dir_block); | 130 | qty /= sizeof(union afs_xdr_dir_block); |
131 | 131 | ||
132 | /* check them */ | 132 | /* check them */ |
133 | dbuf = page_address(page); | 133 | dbuf = kmap(page); |
134 | for (tmp = 0; tmp < qty; tmp++) { | 134 | for (tmp = 0; tmp < qty; tmp++) { |
135 | if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { | 135 | if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) { |
136 | printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", | 136 | printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", |
137 | __func__, dvnode->vfs_inode.i_ino, tmp, qty, | 137 | __func__, dvnode->vfs_inode.i_ino, tmp, qty, |
138 | ntohs(dbuf->blocks[tmp].hdr.magic)); | 138 | ntohs(dbuf->blocks[tmp].hdr.magic)); |
139 | trace_afs_dir_check_failed(dvnode, off, i_size); | 139 | trace_afs_dir_check_failed(dvnode, off, i_size); |
140 | kunmap(page); | ||
140 | goto error; | 141 | goto error; |
141 | } | 142 | } |
143 | |||
144 | /* Make sure each block is NUL terminated so we can reasonably | ||
145 | * use string functions on it. The filenames in the page | ||
146 | * *should* be NUL-terminated anyway. | ||
147 | */ | ||
148 | ((u8 *)&dbuf->blocks[tmp])[AFS_DIR_BLOCK_SIZE - 1] = 0; | ||
142 | } | 149 | } |
143 | 150 | ||
151 | kunmap(page); | ||
152 | |||
144 | checked: | 153 | checked: |
145 | afs_stat_v(dvnode, n_read_dir); | 154 | afs_stat_v(dvnode, n_read_dir); |
146 | return true; | 155 | return true; |
@@ -1114,6 +1123,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
1114 | struct afs_vnode *dvnode = AFS_FS_I(dir); | 1123 | struct afs_vnode *dvnode = AFS_FS_I(dir); |
1115 | struct afs_fid newfid; | 1124 | struct afs_fid newfid; |
1116 | struct key *key; | 1125 | struct key *key; |
1126 | u64 data_version = dvnode->status.data_version; | ||
1117 | int ret; | 1127 | int ret; |
1118 | 1128 | ||
1119 | mode |= S_IFDIR; | 1129 | mode |= S_IFDIR; |
@@ -1131,7 +1141,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
1131 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1141 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1132 | while (afs_select_fileserver(&fc)) { | 1142 | while (afs_select_fileserver(&fc)) { |
1133 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1143 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1134 | afs_fs_create(&fc, dentry->d_name.name, mode, | 1144 | afs_fs_create(&fc, dentry->d_name.name, mode, data_version, |
1135 | &newfid, &newstatus, &newcb); | 1145 | &newfid, &newstatus, &newcb); |
1136 | } | 1146 | } |
1137 | 1147 | ||
@@ -1145,6 +1155,11 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | |||
1145 | goto error_key; | 1155 | goto error_key; |
1146 | } | 1156 | } |
1147 | 1157 | ||
1158 | if (ret == 0 && | ||
1159 | test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1160 | afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||
1161 | afs_edit_dir_for_create); | ||
1162 | |||
1148 | key_put(key); | 1163 | key_put(key); |
1149 | _leave(" = 0"); | 1164 | _leave(" = 0"); |
1150 | return 0; | 1165 | return 0; |
@@ -1168,6 +1183,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry) | |||
1168 | clear_nlink(&vnode->vfs_inode); | 1183 | clear_nlink(&vnode->vfs_inode); |
1169 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | 1184 | set_bit(AFS_VNODE_DELETED, &vnode->flags); |
1170 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); | 1185 | clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); |
1186 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
1171 | } | 1187 | } |
1172 | } | 1188 | } |
1173 | 1189 | ||
@@ -1179,6 +1195,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1179 | struct afs_fs_cursor fc; | 1195 | struct afs_fs_cursor fc; |
1180 | struct afs_vnode *dvnode = AFS_FS_I(dir); | 1196 | struct afs_vnode *dvnode = AFS_FS_I(dir); |
1181 | struct key *key; | 1197 | struct key *key; |
1198 | u64 data_version = dvnode->status.data_version; | ||
1182 | int ret; | 1199 | int ret; |
1183 | 1200 | ||
1184 | _enter("{%x:%u},{%pd}", | 1201 | _enter("{%x:%u},{%pd}", |
@@ -1194,13 +1211,18 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) | |||
1194 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1211 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1195 | while (afs_select_fileserver(&fc)) { | 1212 | while (afs_select_fileserver(&fc)) { |
1196 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1213 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1197 | afs_fs_remove(&fc, dentry->d_name.name, true); | 1214 | afs_fs_remove(&fc, dentry->d_name.name, true, |
1215 | data_version); | ||
1198 | } | 1216 | } |
1199 | 1217 | ||
1200 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | 1218 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); |
1201 | ret = afs_end_vnode_operation(&fc); | 1219 | ret = afs_end_vnode_operation(&fc); |
1202 | if (ret == 0) | 1220 | if (ret == 0) { |
1203 | afs_dir_remove_subdir(dentry); | 1221 | afs_dir_remove_subdir(dentry); |
1222 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1223 | afs_edit_dir_remove(dvnode, &dentry->d_name, | ||
1224 | afs_edit_dir_for_rmdir); | ||
1225 | } | ||
1204 | } | 1226 | } |
1205 | 1227 | ||
1206 | key_put(key); | 1228 | key_put(key); |
@@ -1265,6 +1287,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1265 | struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; | 1287 | struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; |
1266 | struct key *key; | 1288 | struct key *key; |
1267 | unsigned long d_version = (unsigned long)dentry->d_fsdata; | 1289 | unsigned long d_version = (unsigned long)dentry->d_fsdata; |
1290 | u64 data_version = dvnode->status.data_version; | ||
1268 | int ret; | 1291 | int ret; |
1269 | 1292 | ||
1270 | _enter("{%x:%u},{%pd}", | 1293 | _enter("{%x:%u},{%pd}", |
@@ -1291,7 +1314,8 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1291 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1314 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1292 | while (afs_select_fileserver(&fc)) { | 1315 | while (afs_select_fileserver(&fc)) { |
1293 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1316 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1294 | afs_fs_remove(&fc, dentry->d_name.name, false); | 1317 | afs_fs_remove(&fc, dentry->d_name.name, false, |
1318 | data_version); | ||
1295 | } | 1319 | } |
1296 | 1320 | ||
1297 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | 1321 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); |
@@ -1300,6 +1324,10 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) | |||
1300 | ret = afs_dir_remove_link( | 1324 | ret = afs_dir_remove_link( |
1301 | dentry, key, d_version, | 1325 | dentry, key, d_version, |
1302 | (unsigned long)dvnode->status.data_version); | 1326 | (unsigned long)dvnode->status.data_version); |
1327 | if (ret == 0 && | ||
1328 | test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1329 | afs_edit_dir_remove(dvnode, &dentry->d_name, | ||
1330 | afs_edit_dir_for_unlink); | ||
1303 | } | 1331 | } |
1304 | 1332 | ||
1305 | error_key: | 1333 | error_key: |
@@ -1321,6 +1349,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
1321 | struct afs_vnode *dvnode = AFS_FS_I(dir); | 1349 | struct afs_vnode *dvnode = AFS_FS_I(dir); |
1322 | struct afs_fid newfid; | 1350 | struct afs_fid newfid; |
1323 | struct key *key; | 1351 | struct key *key; |
1352 | u64 data_version = dvnode->status.data_version; | ||
1324 | int ret; | 1353 | int ret; |
1325 | 1354 | ||
1326 | mode |= S_IFREG; | 1355 | mode |= S_IFREG; |
@@ -1342,7 +1371,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
1342 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1371 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1343 | while (afs_select_fileserver(&fc)) { | 1372 | while (afs_select_fileserver(&fc)) { |
1344 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1373 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1345 | afs_fs_create(&fc, dentry->d_name.name, mode, | 1374 | afs_fs_create(&fc, dentry->d_name.name, mode, data_version, |
1346 | &newfid, &newstatus, &newcb); | 1375 | &newfid, &newstatus, &newcb); |
1347 | } | 1376 | } |
1348 | 1377 | ||
@@ -1356,6 +1385,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | |||
1356 | goto error_key; | 1385 | goto error_key; |
1357 | } | 1386 | } |
1358 | 1387 | ||
1388 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1389 | afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||
1390 | afs_edit_dir_for_create); | ||
1391 | |||
1359 | key_put(key); | 1392 | key_put(key); |
1360 | _leave(" = 0"); | 1393 | _leave(" = 0"); |
1361 | return 0; | 1394 | return 0; |
@@ -1377,10 +1410,12 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
1377 | struct afs_fs_cursor fc; | 1410 | struct afs_fs_cursor fc; |
1378 | struct afs_vnode *dvnode, *vnode; | 1411 | struct afs_vnode *dvnode, *vnode; |
1379 | struct key *key; | 1412 | struct key *key; |
1413 | u64 data_version; | ||
1380 | int ret; | 1414 | int ret; |
1381 | 1415 | ||
1382 | vnode = AFS_FS_I(d_inode(from)); | 1416 | vnode = AFS_FS_I(d_inode(from)); |
1383 | dvnode = AFS_FS_I(dir); | 1417 | dvnode = AFS_FS_I(dir); |
1418 | data_version = dvnode->status.data_version; | ||
1384 | 1419 | ||
1385 | _enter("{%x:%u},{%x:%u},{%pd}", | 1420 | _enter("{%x:%u},{%x:%u},{%pd}", |
1386 | vnode->fid.vid, vnode->fid.vnode, | 1421 | vnode->fid.vid, vnode->fid.vnode, |
@@ -1407,7 +1442,7 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
1407 | while (afs_select_fileserver(&fc)) { | 1442 | while (afs_select_fileserver(&fc)) { |
1408 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1443 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1409 | fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break; | 1444 | fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break; |
1410 | afs_fs_link(&fc, vnode, dentry->d_name.name); | 1445 | afs_fs_link(&fc, vnode, dentry->d_name.name, data_version); |
1411 | } | 1446 | } |
1412 | 1447 | ||
1413 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); | 1448 | afs_vnode_commit_status(&fc, dvnode, fc.cb_break); |
@@ -1423,6 +1458,10 @@ static int afs_link(struct dentry *from, struct inode *dir, | |||
1423 | goto error_key; | 1458 | goto error_key; |
1424 | } | 1459 | } |
1425 | 1460 | ||
1461 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1462 | afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid, | ||
1463 | afs_edit_dir_for_link); | ||
1464 | |||
1426 | key_put(key); | 1465 | key_put(key); |
1427 | _leave(" = 0"); | 1466 | _leave(" = 0"); |
1428 | return 0; | 1467 | return 0; |
@@ -1446,6 +1485,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
1446 | struct afs_vnode *dvnode = AFS_FS_I(dir); | 1485 | struct afs_vnode *dvnode = AFS_FS_I(dir); |
1447 | struct afs_fid newfid; | 1486 | struct afs_fid newfid; |
1448 | struct key *key; | 1487 | struct key *key; |
1488 | u64 data_version = dvnode->status.data_version; | ||
1449 | int ret; | 1489 | int ret; |
1450 | 1490 | ||
1451 | _enter("{%x:%u},{%pd},%s", | 1491 | _enter("{%x:%u},{%pd},%s", |
@@ -1470,7 +1510,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
1470 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { | 1510 | if (afs_begin_vnode_operation(&fc, dvnode, key)) { |
1471 | while (afs_select_fileserver(&fc)) { | 1511 | while (afs_select_fileserver(&fc)) { |
1472 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; | 1512 | fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; |
1473 | afs_fs_symlink(&fc, dentry->d_name.name, content, | 1513 | afs_fs_symlink(&fc, dentry->d_name.name, |
1514 | content, data_version, | ||
1474 | &newfid, &newstatus); | 1515 | &newfid, &newstatus); |
1475 | } | 1516 | } |
1476 | 1517 | ||
@@ -1484,6 +1525,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, | |||
1484 | goto error_key; | 1525 | goto error_key; |
1485 | } | 1526 | } |
1486 | 1527 | ||
1528 | if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) | ||
1529 | afs_edit_dir_add(dvnode, &dentry->d_name, &newfid, | ||
1530 | afs_edit_dir_for_symlink); | ||
1531 | |||
1487 | key_put(key); | 1532 | key_put(key); |
1488 | _leave(" = 0"); | 1533 | _leave(" = 0"); |
1489 | return 0; | 1534 | return 0; |
@@ -1506,6 +1551,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1506 | struct afs_fs_cursor fc; | 1551 | struct afs_fs_cursor fc; |
1507 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | 1552 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; |
1508 | struct key *key; | 1553 | struct key *key; |
1554 | u64 orig_data_version, new_data_version; | ||
1555 | bool new_negative = d_is_negative(new_dentry); | ||
1509 | int ret; | 1556 | int ret; |
1510 | 1557 | ||
1511 | if (flags) | 1558 | if (flags) |
@@ -1514,6 +1561,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1514 | vnode = AFS_FS_I(d_inode(old_dentry)); | 1561 | vnode = AFS_FS_I(d_inode(old_dentry)); |
1515 | orig_dvnode = AFS_FS_I(old_dir); | 1562 | orig_dvnode = AFS_FS_I(old_dir); |
1516 | new_dvnode = AFS_FS_I(new_dir); | 1563 | new_dvnode = AFS_FS_I(new_dir); |
1564 | orig_data_version = orig_dvnode->status.data_version; | ||
1565 | new_data_version = new_dvnode->status.data_version; | ||
1517 | 1566 | ||
1518 | _enter("{%x:%u},{%x:%u},{%x:%u},{%pd}", | 1567 | _enter("{%x:%u},{%x:%u},{%x:%u},{%pd}", |
1519 | orig_dvnode->fid.vid, orig_dvnode->fid.vnode, | 1568 | orig_dvnode->fid.vid, orig_dvnode->fid.vnode, |
@@ -1539,7 +1588,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1539 | fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break; | 1588 | fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break; |
1540 | fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break; | 1589 | fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break; |
1541 | afs_fs_rename(&fc, old_dentry->d_name.name, | 1590 | afs_fs_rename(&fc, old_dentry->d_name.name, |
1542 | new_dvnode, new_dentry->d_name.name); | 1591 | new_dvnode, new_dentry->d_name.name, |
1592 | orig_data_version, new_data_version); | ||
1543 | } | 1593 | } |
1544 | 1594 | ||
1545 | afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break); | 1595 | afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break); |
@@ -1551,6 +1601,21 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
1551 | goto error_key; | 1601 | goto error_key; |
1552 | } | 1602 | } |
1553 | 1603 | ||
1604 | if (ret == 0) { | ||
1605 | if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags)) | ||
1606 | afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name, | ||
1607 | afs_edit_dir_for_rename); | ||
1608 | |||
1609 | if (!new_negative && | ||
1610 | test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | ||
1611 | afs_edit_dir_remove(new_dvnode, &new_dentry->d_name, | ||
1612 | afs_edit_dir_for_rename); | ||
1613 | |||
1614 | if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags)) | ||
1615 | afs_edit_dir_add(new_dvnode, &new_dentry->d_name, | ||
1616 | &vnode->fid, afs_edit_dir_for_rename); | ||
1617 | } | ||
1618 | |||
1554 | error_key: | 1619 | error_key: |
1555 | key_put(key); | 1620 | key_put(key); |
1556 | error: | 1621 | error: |
diff --git a/fs/afs/dir_edit.c b/fs/afs/dir_edit.c new file mode 100644 index 000000000000..8b400f5aead5 --- /dev/null +++ b/fs/afs/dir_edit.c | |||
@@ -0,0 +1,505 @@ | |||
1 | /* AFS filesystem directory editing | ||
2 | * | ||
3 | * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/namei.h> | ||
15 | #include <linux/pagemap.h> | ||
16 | #include <linux/iversion.h> | ||
17 | #include "internal.h" | ||
18 | #include "xdr_fs.h" | ||
19 | |||
20 | /* | ||
21 | * Find a number of contiguous clear bits in a directory block bitmask. | ||
22 | * | ||
23 | * There are 64 slots, which means we can load the entire bitmap into a | ||
24 | * variable. The first bit doesn't count as it corresponds to the block header | ||
25 | * slot. nr_slots is between 1 and 9. | ||
26 | */ | ||
27 | static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots) | ||
28 | { | ||
29 | u64 bitmap; | ||
30 | u32 mask; | ||
31 | int bit, n; | ||
32 | |||
33 | bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; | ||
34 | bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; | ||
35 | bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; | ||
36 | bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; | ||
37 | bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; | ||
38 | bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; | ||
39 | bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; | ||
40 | bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; | ||
41 | bitmap >>= 1; /* The first entry is metadata */ | ||
42 | bit = 1; | ||
43 | mask = (1 << nr_slots) - 1; | ||
44 | |||
45 | do { | ||
46 | if (sizeof(unsigned long) == 8) | ||
47 | n = ffz(bitmap); | ||
48 | else | ||
49 | n = ((u32)bitmap) != 0 ? | ||
50 | ffz((u32)bitmap) : | ||
51 | ffz((u32)(bitmap >> 32)) + 32; | ||
52 | bitmap >>= n; | ||
53 | bit += n; | ||
54 | |||
55 | if ((bitmap & mask) == 0) { | ||
56 | if (bit > 64 - nr_slots) | ||
57 | return -1; | ||
58 | return bit; | ||
59 | } | ||
60 | |||
61 | n = __ffs(bitmap); | ||
62 | bitmap >>= n; | ||
63 | bit += n; | ||
64 | } while (bitmap); | ||
65 | |||
66 | return -1; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Set a number of contiguous bits in the directory block bitmap. | ||
71 | */ | ||
72 | static void afs_set_contig_bits(union afs_xdr_dir_block *block, | ||
73 | int bit, unsigned int nr_slots) | ||
74 | { | ||
75 | u64 mask, before, after; | ||
76 | |||
77 | mask = (1 << nr_slots) - 1; | ||
78 | mask <<= bit; | ||
79 | |||
80 | before = *(u64 *)block->hdr.bitmap; | ||
81 | |||
82 | block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8); | ||
83 | block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8); | ||
84 | block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8); | ||
85 | block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8); | ||
86 | block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8); | ||
87 | block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8); | ||
88 | block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8); | ||
89 | block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8); | ||
90 | |||
91 | after = *(u64 *)block->hdr.bitmap; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Clear a number of contiguous bits in the directory block bitmap. | ||
96 | */ | ||
97 | static void afs_clear_contig_bits(union afs_xdr_dir_block *block, | ||
98 | int bit, unsigned int nr_slots) | ||
99 | { | ||
100 | u64 mask, before, after; | ||
101 | |||
102 | mask = (1 << nr_slots) - 1; | ||
103 | mask <<= bit; | ||
104 | |||
105 | before = *(u64 *)block->hdr.bitmap; | ||
106 | |||
107 | block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8); | ||
108 | block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8); | ||
109 | block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8); | ||
110 | block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8); | ||
111 | block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8); | ||
112 | block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8); | ||
113 | block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8); | ||
114 | block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8); | ||
115 | |||
116 | after = *(u64 *)block->hdr.bitmap; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Scan a directory block looking for a dirent of the right name. | ||
121 | */ | ||
122 | static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name, | ||
123 | unsigned int blocknum) | ||
124 | { | ||
125 | union afs_xdr_dirent *de; | ||
126 | u64 bitmap; | ||
127 | int d, len, n; | ||
128 | |||
129 | _enter(""); | ||
130 | |||
131 | bitmap = (u64)block->hdr.bitmap[0] << 0 * 8; | ||
132 | bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8; | ||
133 | bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8; | ||
134 | bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8; | ||
135 | bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8; | ||
136 | bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8; | ||
137 | bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8; | ||
138 | bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8; | ||
139 | |||
140 | for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS); | ||
141 | d < AFS_DIR_SLOTS_PER_BLOCK; | ||
142 | d++) { | ||
143 | if (!((bitmap >> d) & 1)) | ||
144 | continue; | ||
145 | de = &block->dirents[d]; | ||
146 | if (de->u.valid != 1) | ||
147 | continue; | ||
148 | |||
149 | /* The block was NUL-terminated by afs_dir_check_page(). */ | ||
150 | len = strlen(de->u.name); | ||
151 | if (len == name->len && | ||
152 | memcmp(de->u.name, name->name, name->len) == 0) | ||
153 | return d; | ||
154 | |||
155 | n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||
156 | n /= AFS_DIR_DIRENT_SIZE; | ||
157 | d += n - 1; | ||
158 | } | ||
159 | |||
160 | return -1; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Initialise a new directory block. Note that block 0 is special and contains | ||
165 | * some extra metadata. | ||
166 | */ | ||
167 | static void afs_edit_init_block(union afs_xdr_dir_block *meta, | ||
168 | union afs_xdr_dir_block *block, int block_num) | ||
169 | { | ||
170 | memset(block, 0, sizeof(*block)); | ||
171 | block->hdr.npages = htons(1); | ||
172 | block->hdr.magic = AFS_DIR_MAGIC; | ||
173 | block->hdr.bitmap[0] = 1; | ||
174 | |||
175 | if (block_num == 0) { | ||
176 | block->hdr.bitmap[0] = 0xff; | ||
177 | block->hdr.bitmap[1] = 0x1f; | ||
178 | memset(block->meta.alloc_ctrs, | ||
179 | AFS_DIR_SLOTS_PER_BLOCK, | ||
180 | sizeof(block->meta.alloc_ctrs)); | ||
181 | meta->meta.alloc_ctrs[0] = | ||
182 | AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0; | ||
183 | } | ||
184 | |||
185 | if (block_num < AFS_DIR_BLOCKS_WITH_CTR) | ||
186 | meta->meta.alloc_ctrs[block_num] = | ||
187 | AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Edit a directory's file data to add a new directory entry. Doing this after | ||
192 | * create, mkdir, symlink, link or rename if the data version number is | ||
193 | * incremented by exactly one avoids the need to re-download the entire | ||
194 | * directory contents. | ||
195 | * | ||
196 | * The caller must hold the inode locked. | ||
197 | */ | ||
198 | void afs_edit_dir_add(struct afs_vnode *vnode, | ||
199 | struct qstr *name, struct afs_fid *new_fid, | ||
200 | enum afs_edit_dir_reason why) | ||
201 | { | ||
202 | union afs_xdr_dir_block *meta, *block; | ||
203 | struct afs_xdr_dir_page *meta_page, *dir_page; | ||
204 | union afs_xdr_dirent *de; | ||
205 | struct page *page0, *page; | ||
206 | unsigned int need_slots, nr_blocks, b; | ||
207 | pgoff_t index; | ||
208 | loff_t i_size; | ||
209 | gfp_t gfp; | ||
210 | int slot; | ||
211 | |||
212 | _enter(",,{%d,%s},", name->len, name->name); | ||
213 | |||
214 | i_size = i_size_read(&vnode->vfs_inode); | ||
215 | if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || | ||
216 | (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { | ||
217 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | gfp = vnode->vfs_inode.i_mapping->gfp_mask; | ||
222 | page0 = find_or_create_page(vnode->vfs_inode.i_mapping, 0, gfp); | ||
223 | if (!page0) { | ||
224 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
225 | _leave(" [fgp]"); | ||
226 | return; | ||
227 | } | ||
228 | |||
229 | /* Work out how many slots we're going to need. */ | ||
230 | need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||
231 | need_slots /= AFS_DIR_DIRENT_SIZE; | ||
232 | |||
233 | meta_page = kmap(page0); | ||
234 | meta = &meta_page->blocks[0]; | ||
235 | if (i_size == 0) | ||
236 | goto new_directory; | ||
237 | nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; | ||
238 | |||
239 | /* Find a block that has sufficient slots available. Each VM page | ||
240 | * contains two or more directory blocks. | ||
241 | */ | ||
242 | for (b = 0; b < nr_blocks + 1; b++) { | ||
243 | /* If the directory extended into a new page, then we need to | ||
244 | * tack a new page on the end. | ||
245 | */ | ||
246 | index = b / AFS_DIR_BLOCKS_PER_PAGE; | ||
247 | if (index == 0) { | ||
248 | page = page0; | ||
249 | dir_page = meta_page; | ||
250 | } else { | ||
251 | if (nr_blocks >= AFS_DIR_MAX_BLOCKS) | ||
252 | goto error; | ||
253 | gfp = vnode->vfs_inode.i_mapping->gfp_mask; | ||
254 | page = find_or_create_page(vnode->vfs_inode.i_mapping, | ||
255 | index, gfp); | ||
256 | if (!page) | ||
257 | goto error; | ||
258 | if (!PagePrivate(page)) { | ||
259 | set_page_private(page, 1); | ||
260 | SetPagePrivate(page); | ||
261 | } | ||
262 | dir_page = kmap(page); | ||
263 | } | ||
264 | |||
265 | /* Abandon the edit if we got a callback break. */ | ||
266 | if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||
267 | goto invalidated; | ||
268 | |||
269 | block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE]; | ||
270 | |||
271 | _debug("block %u: %2u %3u %u", | ||
272 | b, | ||
273 | (b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99, | ||
274 | ntohs(block->hdr.npages), | ||
275 | ntohs(block->hdr.magic)); | ||
276 | |||
277 | /* Initialise the block if necessary. */ | ||
278 | if (b == nr_blocks) { | ||
279 | _debug("init %u", b); | ||
280 | afs_edit_init_block(meta, block, b); | ||
281 | i_size_write(&vnode->vfs_inode, (b + 1) * AFS_DIR_BLOCK_SIZE); | ||
282 | } | ||
283 | |||
284 | /* Only lower dir pages have a counter in the header. */ | ||
285 | if (b >= AFS_DIR_BLOCKS_WITH_CTR || | ||
286 | meta->meta.alloc_ctrs[b] >= need_slots) { | ||
287 | /* We need to try and find one or more consecutive | ||
288 | * slots to hold the entry. | ||
289 | */ | ||
290 | slot = afs_find_contig_bits(block, need_slots); | ||
291 | if (slot >= 0) { | ||
292 | _debug("slot %u", slot); | ||
293 | goto found_space; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | if (page != page0) { | ||
298 | unlock_page(page); | ||
299 | kunmap(page); | ||
300 | put_page(page); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | /* There are no spare slots of sufficient size, yet the operation | ||
305 | * succeeded. Download the directory again. | ||
306 | */ | ||
307 | trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name); | ||
308 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
309 | goto out_unmap; | ||
310 | |||
311 | new_directory: | ||
312 | afs_edit_init_block(meta, meta, 0); | ||
313 | i_size = AFS_DIR_BLOCK_SIZE; | ||
314 | i_size_write(&vnode->vfs_inode, i_size); | ||
315 | slot = AFS_DIR_RESV_BLOCKS0; | ||
316 | page = page0; | ||
317 | block = meta; | ||
318 | nr_blocks = 1; | ||
319 | b = 0; | ||
320 | |||
321 | found_space: | ||
322 | /* Set the dirent slot. */ | ||
323 | trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot, | ||
324 | new_fid->vnode, new_fid->unique, name->name); | ||
325 | de = &block->dirents[slot]; | ||
326 | de->u.valid = 1; | ||
327 | de->u.unused[0] = 0; | ||
328 | de->u.hash_next = 0; // TODO: Really need to maintain this | ||
329 | de->u.vnode = htonl(new_fid->vnode); | ||
330 | de->u.unique = htonl(new_fid->unique); | ||
331 | memcpy(de->u.name, name->name, name->len + 1); | ||
332 | de->u.name[name->len] = 0; | ||
333 | |||
334 | /* Adjust the bitmap. */ | ||
335 | afs_set_contig_bits(block, slot, need_slots); | ||
336 | if (page != page0) { | ||
337 | unlock_page(page); | ||
338 | kunmap(page); | ||
339 | put_page(page); | ||
340 | } | ||
341 | |||
342 | /* Adjust the allocation counter. */ | ||
343 | if (b < AFS_DIR_BLOCKS_WITH_CTR) | ||
344 | meta->meta.alloc_ctrs[b] -= need_slots; | ||
345 | |||
346 | inode_inc_iversion_raw(&vnode->vfs_inode); | ||
347 | afs_stat_v(vnode, n_dir_cr); | ||
348 | _debug("Insert %s in %u[%u]", name->name, b, slot); | ||
349 | |||
350 | out_unmap: | ||
351 | unlock_page(page0); | ||
352 | kunmap(page0); | ||
353 | put_page(page0); | ||
354 | _leave(""); | ||
355 | return; | ||
356 | |||
357 | invalidated: | ||
358 | trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name); | ||
359 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
360 | if (page != page0) { | ||
361 | kunmap(page); | ||
362 | put_page(page); | ||
363 | } | ||
364 | goto out_unmap; | ||
365 | |||
366 | error: | ||
367 | trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name); | ||
368 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
369 | goto out_unmap; | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Edit a directory's file data to remove a new directory entry. Doing this | ||
374 | * after unlink, rmdir or rename if the data version number is incremented by | ||
375 | * exactly one avoids the need to re-download the entire directory contents. | ||
376 | * | ||
377 | * The caller must hold the inode locked. | ||
378 | */ | ||
379 | void afs_edit_dir_remove(struct afs_vnode *vnode, | ||
380 | struct qstr *name, enum afs_edit_dir_reason why) | ||
381 | { | ||
382 | struct afs_xdr_dir_page *meta_page, *dir_page; | ||
383 | union afs_xdr_dir_block *meta, *block; | ||
384 | union afs_xdr_dirent *de; | ||
385 | struct page *page0, *page; | ||
386 | unsigned int need_slots, nr_blocks, b; | ||
387 | pgoff_t index; | ||
388 | loff_t i_size; | ||
389 | int slot; | ||
390 | |||
391 | _enter(",,{%d,%s},", name->len, name->name); | ||
392 | |||
393 | i_size = i_size_read(&vnode->vfs_inode); | ||
394 | if (i_size < AFS_DIR_BLOCK_SIZE || | ||
395 | i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS || | ||
396 | (i_size & (AFS_DIR_BLOCK_SIZE - 1))) { | ||
397 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
398 | return; | ||
399 | } | ||
400 | nr_blocks = i_size / AFS_DIR_BLOCK_SIZE; | ||
401 | |||
402 | page0 = find_lock_page(vnode->vfs_inode.i_mapping, 0); | ||
403 | if (!page0) { | ||
404 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
405 | _leave(" [fgp]"); | ||
406 | return; | ||
407 | } | ||
408 | |||
409 | /* Work out how many slots we're going to discard. */ | ||
410 | need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE); | ||
411 | need_slots /= AFS_DIR_DIRENT_SIZE; | ||
412 | |||
413 | meta_page = kmap(page0); | ||
414 | meta = &meta_page->blocks[0]; | ||
415 | |||
416 | /* Find a page that has sufficient slots available. Each VM page | ||
417 | * contains two or more directory blocks. | ||
418 | */ | ||
419 | for (b = 0; b < nr_blocks; b++) { | ||
420 | index = b / AFS_DIR_BLOCKS_PER_PAGE; | ||
421 | if (index != 0) { | ||
422 | page = find_lock_page(vnode->vfs_inode.i_mapping, index); | ||
423 | if (!page) | ||
424 | goto error; | ||
425 | dir_page = kmap(page); | ||
426 | } else { | ||
427 | page = page0; | ||
428 | dir_page = meta_page; | ||
429 | } | ||
430 | |||
431 | /* Abandon the edit if we got a callback break. */ | ||
432 | if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||
433 | goto invalidated; | ||
434 | |||
435 | block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE]; | ||
436 | |||
437 | if (b > AFS_DIR_BLOCKS_WITH_CTR || | ||
438 | meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) { | ||
439 | slot = afs_dir_scan_block(block, name, b); | ||
440 | if (slot >= 0) | ||
441 | goto found_dirent; | ||
442 | } | ||
443 | |||
444 | if (page != page0) { | ||
445 | unlock_page(page); | ||
446 | kunmap(page); | ||
447 | put_page(page); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | /* Didn't find the dirent to clobber. Download the directory again. */ | ||
452 | trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent, | ||
453 | 0, 0, 0, 0, name->name); | ||
454 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
455 | goto out_unmap; | ||
456 | |||
457 | found_dirent: | ||
458 | de = &block->dirents[slot]; | ||
459 | |||
460 | trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot, | ||
461 | ntohl(de->u.vnode), ntohl(de->u.unique), | ||
462 | name->name); | ||
463 | |||
464 | memset(de, 0, sizeof(*de) * need_slots); | ||
465 | |||
466 | /* Adjust the bitmap. */ | ||
467 | afs_clear_contig_bits(block, slot, need_slots); | ||
468 | if (page != page0) { | ||
469 | unlock_page(page); | ||
470 | kunmap(page); | ||
471 | put_page(page); | ||
472 | } | ||
473 | |||
474 | /* Adjust the allocation counter. */ | ||
475 | if (b < AFS_DIR_BLOCKS_WITH_CTR) | ||
476 | meta->meta.alloc_ctrs[b] += need_slots; | ||
477 | |||
478 | inode_set_iversion_raw(&vnode->vfs_inode, vnode->status.data_version); | ||
479 | afs_stat_v(vnode, n_dir_rm); | ||
480 | _debug("Remove %s from %u[%u]", name->name, b, slot); | ||
481 | |||
482 | out_unmap: | ||
483 | unlock_page(page0); | ||
484 | kunmap(page0); | ||
485 | put_page(page0); | ||
486 | _leave(""); | ||
487 | return; | ||
488 | |||
489 | invalidated: | ||
490 | trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval, | ||
491 | 0, 0, 0, 0, name->name); | ||
492 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
493 | if (page != page0) { | ||
494 | unlock_page(page); | ||
495 | kunmap(page); | ||
496 | put_page(page); | ||
497 | } | ||
498 | goto out_unmap; | ||
499 | |||
500 | error: | ||
501 | trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error, | ||
502 | 0, 0, 0, 0, name->name); | ||
503 | clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags); | ||
504 | goto out_unmap; | ||
505 | } | ||
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b66ff0dc8a5a..20d6304a0d3e 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c | |||
@@ -107,6 +107,13 @@ void afs_update_inode_from_status(struct afs_vnode *vnode, | |||
107 | } else { | 107 | } else { |
108 | set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); | 108 | set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags); |
109 | } | 109 | } |
110 | } else if (vnode->status.type == AFS_FTYPE_DIR) { | ||
111 | /* Expected directory change is handled elsewhere so | ||
112 | * that we can locally edit the directory and save on a | ||
113 | * download. | ||
114 | */ | ||
115 | if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags)) | ||
116 | flags &= ~AFS_VNODE_DATA_CHANGED; | ||
110 | } | 117 | } |
111 | } | 118 | } |
112 | 119 | ||
@@ -190,10 +197,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, | |||
190 | 197 | ||
191 | size = (u64)ntohl(xdr->size_lo); | 198 | size = (u64)ntohl(xdr->size_lo); |
192 | size |= (u64)ntohl(xdr->size_hi) << 32; | 199 | size |= (u64)ntohl(xdr->size_hi) << 32; |
193 | if (size != status->size) { | 200 | status->size = size; |
194 | status->size = size; | ||
195 | flags |= AFS_VNODE_DATA_CHANGED; | ||
196 | } | ||
197 | 201 | ||
198 | data_version = (u64)ntohl(xdr->data_version_lo); | 202 | data_version = (u64)ntohl(xdr->data_version_lo); |
199 | data_version |= (u64)ntohl(xdr->data_version_hi) << 32; | 203 | data_version |= (u64)ntohl(xdr->data_version_hi) << 32; |
@@ -736,6 +740,7 @@ static const struct afs_call_type afs_RXFSMakeDir = { | |||
736 | int afs_fs_create(struct afs_fs_cursor *fc, | 740 | int afs_fs_create(struct afs_fs_cursor *fc, |
737 | const char *name, | 741 | const char *name, |
738 | umode_t mode, | 742 | umode_t mode, |
743 | u64 current_data_version, | ||
739 | struct afs_fid *newfid, | 744 | struct afs_fid *newfid, |
740 | struct afs_file_status *newstatus, | 745 | struct afs_file_status *newstatus, |
741 | struct afs_callback *newcb) | 746 | struct afs_callback *newcb) |
@@ -763,7 +768,7 @@ int afs_fs_create(struct afs_fs_cursor *fc, | |||
763 | call->reply[1] = newfid; | 768 | call->reply[1] = newfid; |
764 | call->reply[2] = newstatus; | 769 | call->reply[2] = newstatus; |
765 | call->reply[3] = newcb; | 770 | call->reply[3] = newcb; |
766 | call->expected_version = vnode->status.data_version; | 771 | call->expected_version = current_data_version + 1; |
767 | 772 | ||
768 | /* marshall the parameters */ | 773 | /* marshall the parameters */ |
769 | bp = call->request; | 774 | bp = call->request; |
@@ -836,7 +841,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = { | |||
836 | /* | 841 | /* |
837 | * remove a file or directory | 842 | * remove a file or directory |
838 | */ | 843 | */ |
839 | int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir) | 844 | int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir, |
845 | u64 current_data_version) | ||
840 | { | 846 | { |
841 | struct afs_vnode *vnode = fc->vnode; | 847 | struct afs_vnode *vnode = fc->vnode; |
842 | struct afs_call *call; | 848 | struct afs_call *call; |
@@ -858,7 +864,7 @@ int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir) | |||
858 | 864 | ||
859 | call->key = fc->key; | 865 | call->key = fc->key; |
860 | call->reply[0] = vnode; | 866 | call->reply[0] = vnode; |
861 | call->expected_version = vnode->status.data_version; | 867 | call->expected_version = current_data_version + 1; |
862 | 868 | ||
863 | /* marshall the parameters */ | 869 | /* marshall the parameters */ |
864 | bp = call->request; | 870 | bp = call->request; |
@@ -920,7 +926,7 @@ static const struct afs_call_type afs_RXFSLink = { | |||
920 | * make a hard link | 926 | * make a hard link |
921 | */ | 927 | */ |
922 | int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, | 928 | int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, |
923 | const char *name) | 929 | const char *name, u64 current_data_version) |
924 | { | 930 | { |
925 | struct afs_vnode *dvnode = fc->vnode; | 931 | struct afs_vnode *dvnode = fc->vnode; |
926 | struct afs_call *call; | 932 | struct afs_call *call; |
@@ -941,7 +947,7 @@ int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, | |||
941 | call->key = fc->key; | 947 | call->key = fc->key; |
942 | call->reply[0] = dvnode; | 948 | call->reply[0] = dvnode; |
943 | call->reply[1] = vnode; | 949 | call->reply[1] = vnode; |
944 | call->expected_version = vnode->status.data_version; | 950 | call->expected_version = current_data_version + 1; |
945 | 951 | ||
946 | /* marshall the parameters */ | 952 | /* marshall the parameters */ |
947 | bp = call->request; | 953 | bp = call->request; |
@@ -1009,6 +1015,7 @@ static const struct afs_call_type afs_RXFSSymlink = { | |||
1009 | int afs_fs_symlink(struct afs_fs_cursor *fc, | 1015 | int afs_fs_symlink(struct afs_fs_cursor *fc, |
1010 | const char *name, | 1016 | const char *name, |
1011 | const char *contents, | 1017 | const char *contents, |
1018 | u64 current_data_version, | ||
1012 | struct afs_fid *newfid, | 1019 | struct afs_fid *newfid, |
1013 | struct afs_file_status *newstatus) | 1020 | struct afs_file_status *newstatus) |
1014 | { | 1021 | { |
@@ -1037,7 +1044,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc, | |||
1037 | call->reply[0] = vnode; | 1044 | call->reply[0] = vnode; |
1038 | call->reply[1] = newfid; | 1045 | call->reply[1] = newfid; |
1039 | call->reply[2] = newstatus; | 1046 | call->reply[2] = newstatus; |
1040 | call->expected_version = vnode->status.data_version; | 1047 | call->expected_version = current_data_version + 1; |
1041 | 1048 | ||
1042 | /* marshall the parameters */ | 1049 | /* marshall the parameters */ |
1043 | bp = call->request; | 1050 | bp = call->request; |
@@ -1117,7 +1124,9 @@ static const struct afs_call_type afs_RXFSRename = { | |||
1117 | int afs_fs_rename(struct afs_fs_cursor *fc, | 1124 | int afs_fs_rename(struct afs_fs_cursor *fc, |
1118 | const char *orig_name, | 1125 | const char *orig_name, |
1119 | struct afs_vnode *new_dvnode, | 1126 | struct afs_vnode *new_dvnode, |
1120 | const char *new_name) | 1127 | const char *new_name, |
1128 | u64 current_orig_data_version, | ||
1129 | u64 current_new_data_version) | ||
1121 | { | 1130 | { |
1122 | struct afs_vnode *orig_dvnode = fc->vnode; | 1131 | struct afs_vnode *orig_dvnode = fc->vnode; |
1123 | struct afs_call *call; | 1132 | struct afs_call *call; |
@@ -1145,8 +1154,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc, | |||
1145 | call->key = fc->key; | 1154 | call->key = fc->key; |
1146 | call->reply[0] = orig_dvnode; | 1155 | call->reply[0] = orig_dvnode; |
1147 | call->reply[1] = new_dvnode; | 1156 | call->reply[1] = new_dvnode; |
1148 | call->expected_version = orig_dvnode->status.data_version; | 1157 | call->expected_version = current_orig_data_version + 1; |
1149 | call->expected_version_2 = new_dvnode->status.data_version; | 1158 | call->expected_version_2 = current_new_data_version + 1; |
1150 | 1159 | ||
1151 | /* marshall the parameters */ | 1160 | /* marshall the parameters */ |
1152 | bp = call->request; | 1161 | bp = call->request; |
diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 69bcfb82dd69..3808dcbbac6d 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c | |||
@@ -395,8 +395,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) | |||
395 | if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { | 395 | if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { |
396 | if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { | 396 | if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { |
397 | vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; | 397 | vnode->cb_s_break = vnode->cb_interest->server->cb_s_break; |
398 | } else if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && | 398 | } else if (vnode->status.type == AFS_FTYPE_DIR && |
399 | !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && | 399 | test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) && |
400 | vnode->cb_expires_at - 10 > now) { | ||
401 | valid = true; | ||
402 | } else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && | ||
400 | vnode->cb_expires_at - 10 > now) { | 403 | vnode->cb_expires_at - 10 > now) { |
401 | valid = true; | 404 | valid = true; |
402 | } | 405 | } |
diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 86f3066d9ab0..703ddb8c5d57 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h | |||
@@ -271,6 +271,8 @@ struct afs_net { | |||
271 | atomic_t n_inval; /* Number of invalidations by the server */ | 271 | atomic_t n_inval; /* Number of invalidations by the server */ |
272 | atomic_t n_relpg; /* Number of invalidations by releasepage */ | 272 | atomic_t n_relpg; /* Number of invalidations by releasepage */ |
273 | atomic_t n_read_dir; /* Number of directory pages read */ | 273 | atomic_t n_read_dir; /* Number of directory pages read */ |
274 | atomic_t n_dir_cr; /* Number of directory entry creation edits */ | ||
275 | atomic_t n_dir_rm; /* Number of directory entry removal edits */ | ||
274 | }; | 276 | }; |
275 | 277 | ||
276 | extern const char afs_init_sysname[]; | 278 | extern const char afs_init_sysname[]; |
@@ -680,6 +682,13 @@ extern const struct dentry_operations afs_fs_dentry_operations; | |||
680 | extern void afs_d_release(struct dentry *); | 682 | extern void afs_d_release(struct dentry *); |
681 | 683 | ||
682 | /* | 684 | /* |
685 | * dir_edit.c | ||
686 | */ | ||
687 | extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *, | ||
688 | enum afs_edit_dir_reason); | ||
689 | extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason); | ||
690 | |||
691 | /* | ||
683 | * dynroot.c | 692 | * dynroot.c |
684 | */ | 693 | */ |
685 | extern const struct file_operations afs_dynroot_file_operations; | 694 | extern const struct file_operations afs_dynroot_file_operations; |
@@ -725,14 +734,14 @@ extern void afs_update_inode_from_status(struct afs_vnode *, struct afs_file_sta | |||
725 | extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool); | 734 | extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool); |
726 | extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *); | 735 | extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *); |
727 | extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *); | 736 | extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *); |
728 | extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, | 737 | extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, u64, |
729 | struct afs_fid *, struct afs_file_status *, struct afs_callback *); | 738 | struct afs_fid *, struct afs_file_status *, struct afs_callback *); |
730 | extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool); | 739 | extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool, u64); |
731 | extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *); | 740 | extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64); |
732 | extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, | 741 | extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64, |
733 | struct afs_fid *, struct afs_file_status *); | 742 | struct afs_fid *, struct afs_file_status *); |
734 | extern int afs_fs_rename(struct afs_fs_cursor *, const char *, | 743 | extern int afs_fs_rename(struct afs_fs_cursor *, const char *, |
735 | struct afs_vnode *, const char *); | 744 | struct afs_vnode *, const char *, u64, u64); |
736 | extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *, | 745 | extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *, |
737 | pgoff_t, pgoff_t, unsigned, unsigned); | 746 | pgoff_t, pgoff_t, unsigned, unsigned); |
738 | extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *); | 747 | extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *); |
diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 6f5a000f44a7..3212bce0d4fb 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c | |||
@@ -918,6 +918,10 @@ static int afs_proc_stats_show(struct seq_file *m, void *v) | |||
918 | 918 | ||
919 | seq_printf(m, "dir-data: rdpg=%u\n", | 919 | seq_printf(m, "dir-data: rdpg=%u\n", |
920 | atomic_read(&net->n_read_dir)); | 920 | atomic_read(&net->n_read_dir)); |
921 | |||
922 | seq_printf(m, "dir-edit: cr=%u rm=%u\n", | ||
923 | atomic_read(&net->n_dir_cr), | ||
924 | atomic_read(&net->n_dir_rm)); | ||
921 | return 0; | 925 | return 0; |
922 | } | 926 | } |
923 | 927 | ||
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 0419b7e1e968..f46dee0f5ced 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h | |||
@@ -63,6 +63,27 @@ enum afs_vl_operation { | |||
63 | afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */ | 63 | afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */ |
64 | }; | 64 | }; |
65 | 65 | ||
66 | enum afs_edit_dir_op { | ||
67 | afs_edit_dir_create, | ||
68 | afs_edit_dir_create_error, | ||
69 | afs_edit_dir_create_inval, | ||
70 | afs_edit_dir_create_nospc, | ||
71 | afs_edit_dir_delete, | ||
72 | afs_edit_dir_delete_error, | ||
73 | afs_edit_dir_delete_inval, | ||
74 | afs_edit_dir_delete_noent, | ||
75 | }; | ||
76 | |||
77 | enum afs_edit_dir_reason { | ||
78 | afs_edit_dir_for_create, | ||
79 | afs_edit_dir_for_link, | ||
80 | afs_edit_dir_for_mkdir, | ||
81 | afs_edit_dir_for_rename, | ||
82 | afs_edit_dir_for_rmdir, | ||
83 | afs_edit_dir_for_symlink, | ||
84 | afs_edit_dir_for_unlink, | ||
85 | }; | ||
86 | |||
66 | #endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */ | 87 | #endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */ |
67 | 88 | ||
68 | /* | 89 | /* |
@@ -106,6 +127,25 @@ enum afs_vl_operation { | |||
106 | EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \ | 127 | EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \ |
107 | E_(afs_VL_GetCapabilities, "VL.GetCapabilities") | 128 | E_(afs_VL_GetCapabilities, "VL.GetCapabilities") |
108 | 129 | ||
130 | #define afs_edit_dir_ops \ | ||
131 | EM(afs_edit_dir_create, "create") \ | ||
132 | EM(afs_edit_dir_create_error, "c_fail") \ | ||
133 | EM(afs_edit_dir_create_inval, "c_invl") \ | ||
134 | EM(afs_edit_dir_create_nospc, "c_nspc") \ | ||
135 | EM(afs_edit_dir_delete, "delete") \ | ||
136 | EM(afs_edit_dir_delete_error, "d_err ") \ | ||
137 | EM(afs_edit_dir_delete_inval, "d_invl") \ | ||
138 | E_(afs_edit_dir_delete_noent, "d_nent") | ||
139 | |||
140 | #define afs_edit_dir_reasons \ | ||
141 | EM(afs_edit_dir_for_create, "Create") \ | ||
142 | EM(afs_edit_dir_for_link, "Link ") \ | ||
143 | EM(afs_edit_dir_for_mkdir, "MkDir ") \ | ||
144 | EM(afs_edit_dir_for_rename, "Rename") \ | ||
145 | EM(afs_edit_dir_for_rmdir, "RmDir ") \ | ||
146 | EM(afs_edit_dir_for_symlink, "Symlnk") \ | ||
147 | E_(afs_edit_dir_for_unlink, "Unlink") | ||
148 | |||
109 | 149 | ||
110 | /* | 150 | /* |
111 | * Export enum symbols via userspace. | 151 | * Export enum symbols via userspace. |
@@ -118,6 +158,8 @@ enum afs_vl_operation { | |||
118 | afs_call_traces; | 158 | afs_call_traces; |
119 | afs_fs_operations; | 159 | afs_fs_operations; |
120 | afs_vl_operations; | 160 | afs_vl_operations; |
161 | afs_edit_dir_ops; | ||
162 | afs_edit_dir_reasons; | ||
121 | 163 | ||
122 | /* | 164 | /* |
123 | * Now redefine the EM() and E_() macros to map the enums to the strings that | 165 | * Now redefine the EM() and E_() macros to map the enums to the strings that |
@@ -464,6 +506,54 @@ TRACE_EVENT(afs_call_state, | |||
464 | __entry->ret, __entry->abort) | 506 | __entry->ret, __entry->abort) |
465 | ); | 507 | ); |
466 | 508 | ||
509 | TRACE_EVENT(afs_edit_dir, | ||
510 | TP_PROTO(struct afs_vnode *dvnode, | ||
511 | enum afs_edit_dir_reason why, | ||
512 | enum afs_edit_dir_op op, | ||
513 | unsigned int block, | ||
514 | unsigned int slot, | ||
515 | unsigned int f_vnode, | ||
516 | unsigned int f_unique, | ||
517 | const char *name), | ||
518 | |||
519 | TP_ARGS(dvnode, why, op, block, slot, f_vnode, f_unique, name), | ||
520 | |||
521 | TP_STRUCT__entry( | ||
522 | __field(unsigned int, vnode ) | ||
523 | __field(unsigned int, unique ) | ||
524 | __field(enum afs_edit_dir_reason, why ) | ||
525 | __field(enum afs_edit_dir_op, op ) | ||
526 | __field(unsigned int, block ) | ||
527 | __field(unsigned short, slot ) | ||
528 | __field(unsigned int, f_vnode ) | ||
529 | __field(unsigned int, f_unique ) | ||
530 | __array(char, name, 18 ) | ||
531 | ), | ||
532 | |||
533 | TP_fast_assign( | ||
534 | int __len = strlen(name); | ||
535 | __len = min(__len, 17); | ||
536 | __entry->vnode = dvnode->fid.vnode; | ||
537 | __entry->unique = dvnode->fid.unique; | ||
538 | __entry->why = why; | ||
539 | __entry->op = op; | ||
540 | __entry->block = block; | ||
541 | __entry->slot = slot; | ||
542 | __entry->f_vnode = f_vnode; | ||
543 | __entry->f_unique = f_unique; | ||
544 | memcpy(__entry->name, name, __len); | ||
545 | __entry->name[__len] = 0; | ||
546 | ), | ||
547 | |||
548 | TP_printk("d=%x:%x %s %s %u[%u] f=%x:%x %s", | ||
549 | __entry->vnode, __entry->unique, | ||
550 | __print_symbolic(__entry->why, afs_edit_dir_reasons), | ||
551 | __print_symbolic(__entry->op, afs_edit_dir_ops), | ||
552 | __entry->block, __entry->slot, | ||
553 | __entry->f_vnode, __entry->f_unique, | ||
554 | __entry->name) | ||
555 | ); | ||
556 | |||
467 | #endif /* _TRACE_AFS_H */ | 557 | #endif /* _TRACE_AFS_H */ |
468 | 558 | ||
469 | /* This part must be outside protection */ | 559 | /* This part must be outside protection */ |