aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ceph
diff options
context:
space:
mode:
authorYan, Zheng <zyan@redhat.com>2019-01-31 03:55:51 -0500
committerIlya Dryomov <idryomov@gmail.com>2019-03-05 12:55:17 -0500
commit37c4efc1ddf98ba8b234d116d863a9464445901e (patch)
tree8eae1cafdd6f7be9fef1844375f41cc23b75a912 /fs/ceph
parent1e9c2eb6811e8e017b589b483f6ff2b7c065eef5 (diff)
ceph: periodically trim stale dentries
Previous commit make VFS delete stale dentry when last reference is dropped. Lease also can become invalid when corresponding dentry has no reference. This patch make cephfs periodically scan lease list, delete corresponding dentry if lease is invalid. There are two types of lease, dentry lease and dir lease. dentry lease has life time and applies to singe dentry. Dentry lease is added to tail of a list when it's updated, leases at front of the list will expire first. Dir lease is CEPH_CAP_FILE_SHARED on directory inode, it applies to all dentries in the directory. Dentries have dir leases are added to another list. Dentries in the list are periodically checked in a round robin manner. Signed-off-by: "Yan, Zheng" <zyan@redhat.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r--fs/ceph/dir.c307
-rw-r--r--fs/ceph/inode.c9
-rw-r--r--fs/ceph/mds_client.c40
-rw-r--r--fs/ceph/mds_client.h9
-rw-r--r--fs/ceph/super.h15
5 files changed, 312 insertions, 68 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index f34a57f56c72..eba283557653 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -29,6 +29,9 @@
29 29
30const struct dentry_operations ceph_dentry_ops; 30const struct dentry_operations ceph_dentry_ops;
31 31
32static bool __dentry_lease_is_valid(struct ceph_dentry_info *di);
33static int __dir_lease_try_check(const struct dentry *dentry);
34
32/* 35/*
33 * Initialize ceph dentry state. 36 * Initialize ceph dentry state.
34 */ 37 */
@@ -44,7 +47,7 @@ static int ceph_d_init(struct dentry *dentry)
44 di->lease_session = NULL; 47 di->lease_session = NULL;
45 di->time = jiffies; 48 di->time = jiffies;
46 dentry->d_fsdata = di; 49 dentry->d_fsdata = di;
47 ceph_dentry_lru_add(dentry); 50 INIT_LIST_HEAD(&di->lease_list);
48 return 0; 51 return 0;
49} 52}
50 53
@@ -241,6 +244,7 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx,
241 goto out; 244 goto out;
242 } 245 }
243 if (fpos_cmp(ctx->pos, di->offset) <= 0) { 246 if (fpos_cmp(ctx->pos, di->offset) <= 0) {
247 __ceph_dentry_dir_lease_touch(di);
244 emit_dentry = true; 248 emit_dentry = true;
245 } 249 }
246 spin_unlock(&dentry->d_lock); 250 spin_unlock(&dentry->d_lock);
@@ -1125,13 +1129,259 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
1125} 1129}
1126 1130
1127/* 1131/*
1132 * Move dentry to tail of mdsc->dentry_leases list when lease is updated.
1133 * Leases at front of the list will expire first. (Assume all leases have
1134 * similar duration)
1135 *
1136 * Called under dentry->d_lock.
1137 */
1138void __ceph_dentry_lease_touch(struct ceph_dentry_info *di)
1139{
1140 struct dentry *dn = di->dentry;
1141 struct ceph_mds_client *mdsc;
1142
1143 dout("dentry_lease_touch %p %p '%pd'\n", di, dn, dn);
1144
1145 di->flags |= CEPH_DENTRY_LEASE_LIST;
1146 if (di->flags & CEPH_DENTRY_SHRINK_LIST) {
1147 di->flags |= CEPH_DENTRY_REFERENCED;
1148 return;
1149 }
1150
1151 mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
1152 spin_lock(&mdsc->dentry_list_lock);
1153 list_move_tail(&di->lease_list, &mdsc->dentry_leases);
1154 spin_unlock(&mdsc->dentry_list_lock);
1155}
1156
1157static void __dentry_dir_lease_touch(struct ceph_mds_client* mdsc,
1158 struct ceph_dentry_info *di)
1159{
1160 di->flags &= ~(CEPH_DENTRY_LEASE_LIST | CEPH_DENTRY_REFERENCED);
1161 di->lease_gen = 0;
1162 di->time = jiffies;
1163 list_move_tail(&di->lease_list, &mdsc->dentry_dir_leases);
1164}
1165
1166/*
1167 * When dir lease is used, add dentry to tail of mdsc->dentry_dir_leases
1168 * list if it's not in the list, otherwise set 'referenced' flag.
1169 *
1170 * Called under dentry->d_lock.
1171 */
1172void __ceph_dentry_dir_lease_touch(struct ceph_dentry_info *di)
1173{
1174 struct dentry *dn = di->dentry;
1175 struct ceph_mds_client *mdsc;
1176
1177 dout("dentry_dir_lease_touch %p %p '%pd' (offset %lld)\n",
1178 di, dn, dn, di->offset);
1179
1180 if (!list_empty(&di->lease_list)) {
1181 if (di->flags & CEPH_DENTRY_LEASE_LIST) {
1182 /* don't remove dentry from dentry lease list
1183 * if its lease is valid */
1184 if (__dentry_lease_is_valid(di))
1185 return;
1186 } else {
1187 di->flags |= CEPH_DENTRY_REFERENCED;
1188 return;
1189 }
1190 }
1191
1192 if (di->flags & CEPH_DENTRY_SHRINK_LIST) {
1193 di->flags |= CEPH_DENTRY_REFERENCED;
1194 di->flags &= ~CEPH_DENTRY_LEASE_LIST;
1195 return;
1196 }
1197
1198 mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
1199 spin_lock(&mdsc->dentry_list_lock);
1200 __dentry_dir_lease_touch(mdsc, di),
1201 spin_unlock(&mdsc->dentry_list_lock);
1202}
1203
1204static void __dentry_lease_unlist(struct ceph_dentry_info *di)
1205{
1206 struct ceph_mds_client *mdsc;
1207 if (di->flags & CEPH_DENTRY_SHRINK_LIST)
1208 return;
1209 if (list_empty(&di->lease_list))
1210 return;
1211
1212 mdsc = ceph_sb_to_client(di->dentry->d_sb)->mdsc;
1213 spin_lock(&mdsc->dentry_list_lock);
1214 list_del_init(&di->lease_list);
1215 spin_unlock(&mdsc->dentry_list_lock);
1216}
1217
1218enum {
1219 KEEP = 0,
1220 DELETE = 1,
1221 TOUCH = 2,
1222 STOP = 4,
1223};
1224
1225struct ceph_lease_walk_control {
1226 bool dir_lease;
1227 unsigned long nr_to_scan;
1228 unsigned long dir_lease_ttl;
1229};
1230
1231static unsigned long
1232__dentry_leases_walk(struct ceph_mds_client *mdsc,
1233 struct ceph_lease_walk_control *lwc,
1234 int (*check)(struct dentry*, void*))
1235{
1236 struct ceph_dentry_info *di, *tmp;
1237 struct dentry *dentry, *last = NULL;
1238 struct list_head* list;
1239 LIST_HEAD(dispose);
1240 unsigned long freed = 0;
1241 int ret = 0;
1242
1243 list = lwc->dir_lease ? &mdsc->dentry_dir_leases : &mdsc->dentry_leases;
1244 spin_lock(&mdsc->dentry_list_lock);
1245 list_for_each_entry_safe(di, tmp, list, lease_list) {
1246 if (!lwc->nr_to_scan)
1247 break;
1248 --lwc->nr_to_scan;
1249
1250 dentry = di->dentry;
1251 if (last == dentry)
1252 break;
1253
1254 if (!spin_trylock(&dentry->d_lock))
1255 continue;
1256
1257 if (dentry->d_lockref.count < 0) {
1258 list_del_init(&di->lease_list);
1259 goto next;
1260 }
1261
1262 ret = check(dentry, lwc);
1263 if (ret & TOUCH) {
1264 /* move it into tail of dir lease list */
1265 __dentry_dir_lease_touch(mdsc, di);
1266 if (!last)
1267 last = dentry;
1268 }
1269 if (ret & DELETE) {
1270 /* stale lease */
1271 di->flags &= ~CEPH_DENTRY_REFERENCED;
1272 if (dentry->d_lockref.count > 0) {
1273 /* update_dentry_lease() will re-add
1274 * it to lease list, or
1275 * ceph_d_delete() will return 1 when
1276 * last reference is dropped */
1277 list_del_init(&di->lease_list);
1278 } else {
1279 di->flags |= CEPH_DENTRY_SHRINK_LIST;
1280 list_move_tail(&di->lease_list, &dispose);
1281 dget_dlock(dentry);
1282 }
1283 }
1284next:
1285 spin_unlock(&dentry->d_lock);
1286 if (ret & STOP)
1287 break;
1288 }
1289 spin_unlock(&mdsc->dentry_list_lock);
1290
1291 while (!list_empty(&dispose)) {
1292 di = list_first_entry(&dispose, struct ceph_dentry_info,
1293 lease_list);
1294 dentry = di->dentry;
1295 spin_lock(&dentry->d_lock);
1296
1297 list_del_init(&di->lease_list);
1298 di->flags &= ~CEPH_DENTRY_SHRINK_LIST;
1299 if (di->flags & CEPH_DENTRY_REFERENCED) {
1300 spin_lock(&mdsc->dentry_list_lock);
1301 if (di->flags & CEPH_DENTRY_LEASE_LIST) {
1302 list_add_tail(&di->lease_list,
1303 &mdsc->dentry_leases);
1304 } else {
1305 __dentry_dir_lease_touch(mdsc, di);
1306 }
1307 spin_unlock(&mdsc->dentry_list_lock);
1308 } else {
1309 freed++;
1310 }
1311
1312 spin_unlock(&dentry->d_lock);
1313 /* ceph_d_delete() does the trick */
1314 dput(dentry);
1315 }
1316 return freed;
1317}
1318
1319static int __dentry_lease_check(struct dentry *dentry, void *arg)
1320{
1321 struct ceph_dentry_info *di = ceph_dentry(dentry);
1322 int ret;
1323
1324 if (__dentry_lease_is_valid(di))
1325 return STOP;
1326 ret = __dir_lease_try_check(dentry);
1327 if (ret == -EBUSY)
1328 return KEEP;
1329 if (ret > 0)
1330 return TOUCH;
1331 return DELETE;
1332}
1333
1334static int __dir_lease_check(struct dentry *dentry, void *arg)
1335{
1336 struct ceph_lease_walk_control *lwc = arg;
1337 struct ceph_dentry_info *di = ceph_dentry(dentry);
1338
1339 int ret = __dir_lease_try_check(dentry);
1340 if (ret == -EBUSY)
1341 return KEEP;
1342 if (ret > 0) {
1343 if (time_before(jiffies, di->time + lwc->dir_lease_ttl))
1344 return STOP;
1345 /* Move dentry to tail of dir lease list if we don't want
1346 * to delete it. So dentries in the list are checked in a
1347 * round robin manner */
1348 return TOUCH;
1349 }
1350 return DELETE;
1351}
1352
1353int ceph_trim_dentries(struct ceph_mds_client *mdsc)
1354{
1355 struct ceph_lease_walk_control lwc;
1356 unsigned long freed;
1357
1358 lwc.dir_lease = false;
1359 lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE * 2;
1360 freed = __dentry_leases_walk(mdsc, &lwc, __dentry_lease_check);
1361 if (!lwc.nr_to_scan) /* more invalid leases */
1362 return -EAGAIN;
1363
1364 if (lwc.nr_to_scan < CEPH_CAPS_PER_RELEASE)
1365 lwc.nr_to_scan = CEPH_CAPS_PER_RELEASE;
1366
1367 lwc.dir_lease = true;
1368 freed +=__dentry_leases_walk(mdsc, &lwc, __dir_lease_check);
1369 if (!lwc.nr_to_scan) /* more to check */
1370 return -EAGAIN;
1371
1372 return freed > 0 ? 1 : 0;
1373}
1374
1375/*
1128 * Ensure a dentry lease will no longer revalidate. 1376 * Ensure a dentry lease will no longer revalidate.
1129 */ 1377 */
1130void ceph_invalidate_dentry_lease(struct dentry *dentry) 1378void ceph_invalidate_dentry_lease(struct dentry *dentry)
1131{ 1379{
1380 struct ceph_dentry_info *di = ceph_dentry(dentry);
1132 spin_lock(&dentry->d_lock); 1381 spin_lock(&dentry->d_lock);
1133 ceph_dentry(dentry)->time = jiffies; 1382 di->time = jiffies;
1134 ceph_dentry(dentry)->lease_shared_gen = 0; 1383 di->lease_shared_gen = 0;
1384 __dentry_lease_unlist(di);
1135 spin_unlock(&dentry->d_lock); 1385 spin_unlock(&dentry->d_lock);
1136} 1386}
1137 1387
@@ -1251,6 +1501,8 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry)
1251 if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen) 1501 if (atomic_read(&ci->i_shared_gen) == di->lease_shared_gen)
1252 valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1); 1502 valid = __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1);
1253 spin_unlock(&ci->i_ceph_lock); 1503 spin_unlock(&ci->i_ceph_lock);
1504 if (valid)
1505 __ceph_dentry_dir_lease_touch(di);
1254 dout("dir_lease_is_valid dir %p v%u dentry %p v%u = %d\n", 1506 dout("dir_lease_is_valid dir %p v%u dentry %p v%u = %d\n",
1255 dir, (unsigned)atomic_read(&ci->i_shared_gen), 1507 dir, (unsigned)atomic_read(&ci->i_shared_gen),
1256 dentry, (unsigned)di->lease_shared_gen, valid); 1508 dentry, (unsigned)di->lease_shared_gen, valid);
@@ -1343,11 +1595,8 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags)
1343 } 1595 }
1344 1596
1345 dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid"); 1597 dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid");
1346 if (valid) { 1598 if (!valid)
1347 ceph_dentry_lru_touch(dentry);
1348 } else {
1349 ceph_dir_clear_complete(dir); 1599 ceph_dir_clear_complete(dir);
1350 }
1351 1600
1352 if (!(flags & LOOKUP_RCU)) 1601 if (!(flags & LOOKUP_RCU))
1353 dput(parent); 1602 dput(parent);
@@ -1387,9 +1636,9 @@ static void ceph_d_release(struct dentry *dentry)
1387 struct ceph_dentry_info *di = ceph_dentry(dentry); 1636 struct ceph_dentry_info *di = ceph_dentry(dentry);
1388 1637
1389 dout("d_release %p\n", dentry); 1638 dout("d_release %p\n", dentry);
1390 ceph_dentry_lru_del(dentry);
1391 1639
1392 spin_lock(&dentry->d_lock); 1640 spin_lock(&dentry->d_lock);
1641 __dentry_lease_unlist(di);
1393 dentry->d_fsdata = NULL; 1642 dentry->d_fsdata = NULL;
1394 spin_unlock(&dentry->d_lock); 1643 spin_unlock(&dentry->d_lock);
1395 1644
@@ -1490,49 +1739,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
1490 return size - left; 1739 return size - left;
1491} 1740}
1492 1741
1493/*
1494 * We maintain a private dentry LRU.
1495 *
1496 * FIXME: this needs to be changed to a per-mds lru to be useful.
1497 */
1498void ceph_dentry_lru_add(struct dentry *dn)
1499{
1500 struct ceph_dentry_info *di = ceph_dentry(dn);
1501 struct ceph_mds_client *mdsc;
1502 1742
1503 dout("dentry_lru_add %p %p '%pd'\n", di, dn, dn);
1504 mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
1505 spin_lock(&mdsc->dentry_lru_lock);
1506 list_add_tail(&di->lru, &mdsc->dentry_lru);
1507 mdsc->num_dentry++;
1508 spin_unlock(&mdsc->dentry_lru_lock);
1509}
1510
1511void ceph_dentry_lru_touch(struct dentry *dn)
1512{
1513 struct ceph_dentry_info *di = ceph_dentry(dn);
1514 struct ceph_mds_client *mdsc;
1515
1516 dout("dentry_lru_touch %p %p '%pd' (offset %lld)\n", di, dn, dn,
1517 di->offset);
1518 mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
1519 spin_lock(&mdsc->dentry_lru_lock);
1520 list_move_tail(&di->lru, &mdsc->dentry_lru);
1521 spin_unlock(&mdsc->dentry_lru_lock);
1522}
1523
1524void ceph_dentry_lru_del(struct dentry *dn)
1525{
1526 struct ceph_dentry_info *di = ceph_dentry(dn);
1527 struct ceph_mds_client *mdsc;
1528
1529 dout("dentry_lru_del %p %p '%pd'\n", di, dn, dn);
1530 mdsc = ceph_sb_to_client(dn->d_sb)->mdsc;
1531 spin_lock(&mdsc->dentry_lru_lock);
1532 list_del_init(&di->lru);
1533 mdsc->num_dentry--;
1534 spin_unlock(&mdsc->dentry_lru_lock);
1535}
1536 1743
1537/* 1744/*
1538 * Return name hash for a given dentry. This is dependent on 1745 * Return name hash for a given dentry. This is dependent on
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index f75476e94d75..e3346628efe2 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1076,9 +1076,10 @@ static void update_dentry_lease(struct dentry *dentry,
1076 goto out_unlock; 1076 goto out_unlock;
1077 1077
1078 di->lease_shared_gen = atomic_read(&ceph_inode(dir)->i_shared_gen); 1078 di->lease_shared_gen = atomic_read(&ceph_inode(dir)->i_shared_gen);
1079 1079 if (duration == 0) {
1080 if (duration == 0) 1080 __ceph_dentry_dir_lease_touch(di);
1081 goto out_unlock; 1081 goto out_unlock;
1082 }
1082 1083
1083 if (di->lease_gen == session->s_cap_gen && 1084 if (di->lease_gen == session->s_cap_gen &&
1084 time_before(ttl, di->time)) 1085 time_before(ttl, di->time))
@@ -1089,8 +1090,6 @@ static void update_dentry_lease(struct dentry *dentry,
1089 di->lease_session = NULL; 1090 di->lease_session = NULL;
1090 } 1091 }
1091 1092
1092 ceph_dentry_lru_touch(dentry);
1093
1094 if (!di->lease_session) 1093 if (!di->lease_session)
1095 di->lease_session = ceph_get_mds_session(session); 1094 di->lease_session = ceph_get_mds_session(session);
1096 di->lease_gen = session->s_cap_gen; 1095 di->lease_gen = session->s_cap_gen;
@@ -1098,6 +1097,8 @@ static void update_dentry_lease(struct dentry *dentry,
1098 di->lease_renew_after = half_ttl; 1097 di->lease_renew_after = half_ttl;
1099 di->lease_renew_from = 0; 1098 di->lease_renew_from = 0;
1100 di->time = ttl; 1099 di->time = ttl;
1100
1101 __ceph_dentry_lease_touch(di);
1101out_unlock: 1102out_unlock:
1102 spin_unlock(&dentry->d_lock); 1103 spin_unlock(&dentry->d_lock);
1103 if (old_lease_session) 1104 if (old_lease_session)
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 99d6e805668c..2095e5d038f8 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -58,6 +58,7 @@ struct ceph_reconnect_state {
58static void __wake_requests(struct ceph_mds_client *mdsc, 58static void __wake_requests(struct ceph_mds_client *mdsc,
59 struct list_head *head); 59 struct list_head *head);
60static void ceph_cap_release_work(struct work_struct *work); 60static void ceph_cap_release_work(struct work_struct *work);
61static void ceph_cap_reclaim_work(struct work_struct *work);
61 62
62static const struct ceph_connection_operations mds_con_ops; 63static const struct ceph_connection_operations mds_con_ops;
63 64
@@ -1943,6 +1944,27 @@ void __ceph_queue_cap_release(struct ceph_mds_session *session,
1943 ceph_flush_cap_releases(session->s_mdsc, session); 1944 ceph_flush_cap_releases(session->s_mdsc, session);
1944} 1945}
1945 1946
1947static void ceph_cap_reclaim_work(struct work_struct *work)
1948{
1949 struct ceph_mds_client *mdsc =
1950 container_of(work, struct ceph_mds_client, cap_reclaim_work);
1951 int ret = ceph_trim_dentries(mdsc);
1952 if (ret == -EAGAIN)
1953 ceph_queue_cap_reclaim_work(mdsc);
1954}
1955
1956void ceph_queue_cap_reclaim_work(struct ceph_mds_client *mdsc)
1957{
1958 if (mdsc->stopping)
1959 return;
1960
1961 if (queue_work(mdsc->fsc->cap_wq, &mdsc->cap_reclaim_work)) {
1962 dout("caps reclaim work queued\n");
1963 } else {
1964 dout("failed to queue caps release work\n");
1965 }
1966}
1967
1946/* 1968/*
1947 * requests 1969 * requests
1948 */ 1970 */
@@ -3957,9 +3979,6 @@ static void delayed_work(struct work_struct *work)
3957 int renew_caps; 3979 int renew_caps;
3958 3980
3959 dout("mdsc delayed_work\n"); 3981 dout("mdsc delayed_work\n");
3960 ceph_check_delayed_caps(mdsc);
3961
3962 ceph_trim_snapid_map(mdsc);
3963 3982
3964 mutex_lock(&mdsc->mutex); 3983 mutex_lock(&mdsc->mutex);
3965 renew_interval = mdsc->mdsmap->m_session_timeout >> 2; 3984 renew_interval = mdsc->mdsmap->m_session_timeout >> 2;
@@ -4007,6 +4026,12 @@ static void delayed_work(struct work_struct *work)
4007 } 4026 }
4008 mutex_unlock(&mdsc->mutex); 4027 mutex_unlock(&mdsc->mutex);
4009 4028
4029 ceph_check_delayed_caps(mdsc);
4030
4031 ceph_queue_cap_reclaim_work(mdsc);
4032
4033 ceph_trim_snapid_map(mdsc);
4034
4010 schedule_delayed(mdsc); 4035 schedule_delayed(mdsc);
4011} 4036}
4012 4037
@@ -4057,8 +4082,11 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc)
4057 mdsc->num_cap_flushing = 0; 4082 mdsc->num_cap_flushing = 0;
4058 spin_lock_init(&mdsc->cap_dirty_lock); 4083 spin_lock_init(&mdsc->cap_dirty_lock);
4059 init_waitqueue_head(&mdsc->cap_flushing_wq); 4084 init_waitqueue_head(&mdsc->cap_flushing_wq);
4060 spin_lock_init(&mdsc->dentry_lru_lock); 4085 INIT_WORK(&mdsc->cap_reclaim_work, ceph_cap_reclaim_work);
4061 INIT_LIST_HEAD(&mdsc->dentry_lru); 4086
4087 spin_lock_init(&mdsc->dentry_list_lock);
4088 INIT_LIST_HEAD(&mdsc->dentry_leases);
4089 INIT_LIST_HEAD(&mdsc->dentry_dir_leases);
4062 4090
4063 ceph_caps_init(mdsc); 4091 ceph_caps_init(mdsc);
4064 ceph_adjust_min_caps(mdsc, fsc->min_caps); 4092 ceph_adjust_min_caps(mdsc, fsc->min_caps);
@@ -4261,9 +4289,9 @@ void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc)
4261 mutex_unlock(&mdsc->mutex); 4289 mutex_unlock(&mdsc->mutex);
4262 4290
4263 ceph_cleanup_snapid_map(mdsc); 4291 ceph_cleanup_snapid_map(mdsc);
4264
4265 ceph_cleanup_empty_realms(mdsc); 4292 ceph_cleanup_empty_realms(mdsc);
4266 4293
4294 cancel_work_sync(&mdsc->cap_reclaim_work);
4267 cancel_delayed_work_sync(&mdsc->delayed_work); /* cancel timer */ 4295 cancel_delayed_work_sync(&mdsc->delayed_work); /* cancel timer */
4268 4296
4269 dout("stopped\n"); 4297 dout("stopped\n");
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 2147ecd0c9e5..580b235f343b 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -378,6 +378,8 @@ struct ceph_mds_client {
378 spinlock_t cap_dirty_lock; /* protects above items */ 378 spinlock_t cap_dirty_lock; /* protects above items */
379 wait_queue_head_t cap_flushing_wq; 379 wait_queue_head_t cap_flushing_wq;
380 380
381 struct work_struct cap_reclaim_work;
382
381 /* 383 /*
382 * Cap reservations 384 * Cap reservations
383 * 385 *
@@ -398,9 +400,9 @@ struct ceph_mds_client {
398 int caps_avail_count; /* unused, unreserved */ 400 int caps_avail_count; /* unused, unreserved */
399 int caps_min_count; /* keep at least this many 401 int caps_min_count; /* keep at least this many
400 (unreserved) */ 402 (unreserved) */
401 spinlock_t dentry_lru_lock; 403 spinlock_t dentry_list_lock;
402 struct list_head dentry_lru; 404 struct list_head dentry_leases; /* fifo list */
403 int num_dentry; 405 struct list_head dentry_dir_leases; /* lru list */
404 406
405 spinlock_t snapid_map_lock; 407 spinlock_t snapid_map_lock;
406 struct rb_root snapid_map_tree; 408 struct rb_root snapid_map_tree;
@@ -462,6 +464,7 @@ extern void __ceph_queue_cap_release(struct ceph_mds_session *session,
462 struct ceph_cap *cap); 464 struct ceph_cap *cap);
463extern void ceph_flush_cap_releases(struct ceph_mds_client *mdsc, 465extern void ceph_flush_cap_releases(struct ceph_mds_client *mdsc,
464 struct ceph_mds_session *session); 466 struct ceph_mds_session *session);
467extern void ceph_queue_cap_reclaim_work(struct ceph_mds_client *mdsc);
465extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc); 468extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc);
466 469
467extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, 470extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index c0654e613fc0..b3bcfb3c27bd 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -262,17 +262,22 @@ struct ceph_inode_xattr {
262 * Ceph dentry state 262 * Ceph dentry state
263 */ 263 */
264struct ceph_dentry_info { 264struct ceph_dentry_info {
265 struct dentry *dentry;
265 struct ceph_mds_session *lease_session; 266 struct ceph_mds_session *lease_session;
267 struct list_head lease_list;
268 unsigned flags;
266 int lease_shared_gen; 269 int lease_shared_gen;
267 u32 lease_gen; 270 u32 lease_gen;
268 u32 lease_seq; 271 u32 lease_seq;
269 unsigned long lease_renew_after, lease_renew_from; 272 unsigned long lease_renew_after, lease_renew_from;
270 struct list_head lru;
271 struct dentry *dentry;
272 unsigned long time; 273 unsigned long time;
273 u64 offset; 274 u64 offset;
274}; 275};
275 276
277#define CEPH_DENTRY_REFERENCED 1
278#define CEPH_DENTRY_LEASE_LIST 2
279#define CEPH_DENTRY_SHRINK_LIST 4
280
276struct ceph_inode_xattrs_info { 281struct ceph_inode_xattrs_info {
277 /* 282 /*
278 * (still encoded) xattr blob. we avoid the overhead of parsing 283 * (still encoded) xattr blob. we avoid the overhead of parsing
@@ -1064,10 +1069,10 @@ extern int ceph_handle_snapdir(struct ceph_mds_request *req,
1064extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, 1069extern struct dentry *ceph_finish_lookup(struct ceph_mds_request *req,
1065 struct dentry *dentry, int err); 1070 struct dentry *dentry, int err);
1066 1071
1067extern void ceph_dentry_lru_add(struct dentry *dn); 1072extern void __ceph_dentry_lease_touch(struct ceph_dentry_info *di);
1068extern void ceph_dentry_lru_touch(struct dentry *dn); 1073extern void __ceph_dentry_dir_lease_touch(struct ceph_dentry_info *di);
1069extern void ceph_dentry_lru_del(struct dentry *dn);
1070extern void ceph_invalidate_dentry_lease(struct dentry *dentry); 1074extern void ceph_invalidate_dentry_lease(struct dentry *dentry);
1075extern int ceph_trim_dentries(struct ceph_mds_client *mdsc);
1071extern unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn); 1076extern unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn);
1072extern void ceph_readdir_cache_release(struct ceph_readdir_cache_control *ctl); 1077extern void ceph_readdir_cache_release(struct ceph_readdir_cache_control *ctl);
1073 1078