History

ceph: pre-allocate data structure that tracks caps flushing

Yan, Zheng committed 11 years ago

Commit f66fd9f1 parent e548e9b9 files · +103 −16Browse files

Unified · Split

9 files
  1. fs/ceph/addr.c +15 −4
  2. fs/ceph/caps.c +22 −4
  3. fs/ceph/file.c +16 −2
  4. fs/ceph/inode.c +13 −2
  5. fs/ceph/mds_client.c +5 −1
  6. fs/ceph/super.c +8 −0
  7. fs/ceph/super.h +5 −1
  8. fs/ceph/xattr.c +18 −2
  9. include/linux/ceph/libceph.h +1 −0
fs/ceph/addr.c+15−4
@@ -1308,12 +1308,17 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) · 1307 unmodified lines
13081308 struct inode *inode = file_inode(vma->vm_file);
13091309 struct ceph_inode_info *ci = ceph_inode(inode);
13101310 struct ceph_file_info *fi = vma->vm_file->private_data;
1311+ struct ceph_cap_flush *prealloc_cf;
13111312 struct page *page = vmf->page;
13121313 loff_t off = page_offset(page);
13131314 loff_t size = i_size_read(inode);
13141315 size_t len;
13151316 int want, got, ret;
13161317
1318+ prealloc_cf = ceph_alloc_cap_flush();
1319+ if (!prealloc_cf)
1320+ return VM_FAULT_SIGBUS;
1321+
13171322 if (ci->i_inline_version != CEPH_INLINE_NONE) {
13181323 struct page *locked_page = NULL;
13191324 if (off == 0) {
@@ -1323,8 +1328,10 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) · 3 unmodified lines
13231328 ret = ceph_uninline_data(vma->vm_file, locked_page);
13241329 if (locked_page)
13251330 unlock_page(locked_page);
1326− if (ret < 0)
1327− return VM_FAULT_SIGBUS;
1331+ if (ret < 0) {
1332+ ret = VM_FAULT_SIGBUS;
1333+ goto out_free;
1334+ }
13281335 }
13291336
13301337 if (off + PAGE_CACHE_SIZE <= size)
@@ -1346,7 +1353,8 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) · 15 unmodified lines
13461353 break;
13471354 if (ret != -ERESTARTSYS) {
13481355 WARN_ON(1);
1349− return VM_FAULT_SIGBUS;
1356+ ret = VM_FAULT_SIGBUS;
1357+ goto out_free;
13501358 }
13511359 }
13521360 dout("page_mkwrite %p %llu~%zd got cap refs on %s\n",
@@ -1381,7 +1389,8 @@ out: · 28 unmodified lines
13811389 int dirty;
13821390 spin_lock(&ci->i_ceph_lock);
13831391 ci->i_inline_version = CEPH_INLINE_NONE;
1384− dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
1392+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR,
1393+ &prealloc_cf);
13851394 spin_unlock(&ci->i_ceph_lock);
13861395 if (dirty)
13871396 __mark_inode_dirty(inode, dirty);
@@ -1390,6 +1399,8 @@ out: · 2 unmodified lines
13901399 dout("page_mkwrite %p %llu~%zd dropping cap refs on %s ret %d\n",
13911400 inode, off, len, ceph_cap_string(got), ret);
13921401 ceph_put_cap_refs(ci, got);
1402+out_free:
1403+ ceph_free_cap_flush(prealloc_cf);
13931404
13941405 return ret;
13951406 }
fs/ceph/caps.c+22−4
@@ -1356,7 +1356,8 @@ static void ceph_flush_snaps(struct ceph_inode_info *ci) · 1355 unmodified lines
13561356 * Caller is then responsible for calling __mark_inode_dirty with the
13571357 * returned flags value.
13581358 */
1359−int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask)
1359+int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask,
1360+ struct ceph_cap_flush **pcf)
13601361 {
13611362 struct ceph_mds_client *mdsc =
13621363 ceph_sb_to_client(ci->vfs_inode.i_sb)->mdsc;
@@ -1376,6 +1377,9 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) · 13 unmodified lines
13761377 ceph_cap_string(was | mask));
13771378 ci->i_dirty_caps |= mask;
13781379 if (was == 0) {
1380+ WARN_ON_ONCE(ci->i_prealloc_cap_flush);
1381+ swap(ci->i_prealloc_cap_flush, *pcf);
1382+
13791383 if (!ci->i_head_snapc) {
13801384 WARN_ON_ONCE(!rwsem_is_locked(&mdsc->snap_rwsem));
13811385 ci->i_head_snapc = ceph_get_snap_context(
@@ -1391,6 +1395,8 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) · 9 unmodified lines
13911395 ihold(inode);
13921396 dirty |= I_DIRTY_SYNC;
13931397 }
1398+ } else {
1399+ WARN_ON_ONCE(!ci->i_prealloc_cap_flush);
13941400 }
13951401 BUG_ON(list_empty(&ci->i_dirty_item));
13961402 if (((was | ci->i_flushing_caps) & CEPH_CAP_FILE_BUFFER) &&
@@ -1446,6 +1452,17 @@ static void __add_cap_flushing_to_mdsc(struct ceph_mds_client *mdsc, · 49 unmodified lines
14461452 rb_insert_color(&cf->g_node, &mdsc->cap_flush_tree);
14471453 }
14481454
1455+struct ceph_cap_flush *ceph_alloc_cap_flush(void)
1456+{
1457+ return kmem_cache_alloc(ceph_cap_flush_cachep, GFP_KERNEL);
1458+}
1459+
1460+void ceph_free_cap_flush(struct ceph_cap_flush *cf)
1461+{
1462+ if (cf)
1463+ kmem_cache_free(ceph_cap_flush_cachep, cf);
1464+}
1465+
14491466 static u64 __get_oldest_flush_tid(struct ceph_mds_client *mdsc)
14501467 {
14511468 struct rb_node *n = rb_first(&mdsc->cap_flush_tree);
@@ -1469,11 +1486,12 @@ static int __mark_caps_flushing(struct inode *inode, · 17 unmodified lines
14691486 {
14701487 struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
14711488 struct ceph_inode_info *ci = ceph_inode(inode);
1472− struct ceph_cap_flush *cf;
1489+ struct ceph_cap_flush *cf = NULL;
14731490 int flushing;
14741491
14751492 BUG_ON(ci->i_dirty_caps == 0);
14761493 BUG_ON(list_empty(&ci->i_dirty_item));
1494+ BUG_ON(!ci->i_prealloc_cap_flush);
14771495
14781496 flushing = ci->i_dirty_caps;
14791497 dout("__mark_caps_flushing flushing %s, flushing_caps %s -> %s\n",
@@ -1484,7 +1502,7 @@ static int __mark_caps_flushing(struct inode *inode, · 4 unmodified lines
14841502 ci->i_dirty_caps = 0;
14851503 dout(" inode %p now !dirty\n", inode);
14861504
1487− cf = kmalloc(sizeof(*cf), GFP_ATOMIC);
1505+ swap(cf, ci->i_prealloc_cap_flush);
14881506 cf->caps = flushing;
14891507 cf->kick = false;
14901508
@@ -3075,7 +3093,7 @@ out: · 1584 unmodified lines
30753093 cf = list_first_entry(&to_remove,
30763094 struct ceph_cap_flush, list);
30773095 list_del(&cf->list);
3078− kfree(cf);
3096+ ceph_free_cap_flush(cf);
30793097 }
30803098 if (drop)
30813099 iput(inode);
fs/ceph/file.c+16−2
@@ -939,6 +939,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) · 938 unmodified lines
939939 struct ceph_inode_info *ci = ceph_inode(inode);
940940 struct ceph_osd_client *osdc =
941941 &ceph_sb_to_client(inode->i_sb)->client->osdc;
942+ struct ceph_cap_flush *prealloc_cf;
942943 ssize_t count, written = 0;
943944 int err, want, got;
944945 loff_t pos;
@@ -946,6 +947,10 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) · 1 unmodified line
946947 if (ceph_snap(inode) != CEPH_NOSNAP)
947948 return -EROFS;
948949
950+ prealloc_cf = ceph_alloc_cap_flush();
951+ if (!prealloc_cf)
952+ return -ENOMEM;
953+
949954 mutex_lock(&inode->i_mutex);
950955
951956 /* We can write back this queue in page reclaim */
@@ -1050,7 +1055,8 @@ retry_snap: · 98 unmodified lines
10501055 int dirty;
10511056 spin_lock(&ci->i_ceph_lock);
10521057 ci->i_inline_version = CEPH_INLINE_NONE;
1053− dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
1058+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR,
1059+ &prealloc_cf);
10541060 spin_unlock(&ci->i_ceph_lock);
10551061 if (dirty)
10561062 __mark_inode_dirty(inode, dirty);
@@ -1074,6 +1080,7 @@ retry_snap: · 17 unmodified lines
10741080 out:
10751081 mutex_unlock(&inode->i_mutex);
10761082 out_unlocked:
1083+ ceph_free_cap_flush(prealloc_cf);
10771084 current->backing_dev_info = NULL;
10781085 return written ? written : err;
10791086 }
@@ -1270,6 +1277,7 @@ static long ceph_fallocate(struct file *file, int mode, · 190 unmodified lines
12701277 struct ceph_inode_info *ci = ceph_inode(inode);
12711278 struct ceph_osd_client *osdc =
12721279 &ceph_inode_to_client(inode)->client->osdc;
1280+ struct ceph_cap_flush *prealloc_cf;
12731281 int want, got = 0;
12741282 int dirty;
12751283 int ret = 0;
@@ -1282,6 +1290,10 @@ static long ceph_fallocate(struct file *file, int mode, · 6 unmodified lines
12821290 if (!S_ISREG(inode->i_mode))
12831291 return -EOPNOTSUPP;
12841292
1293+ prealloc_cf = ceph_alloc_cap_flush();
1294+ if (!prealloc_cf)
1295+ return -ENOMEM;
1296+
12851297 mutex_lock(&inode->i_mutex);
12861298
12871299 if (ceph_snap(inode) != CEPH_NOSNAP) {
@@ -1328,7 +1340,8 @@ static long ceph_fallocate(struct file *file, int mode, · 40 unmodified lines
13281340 if (!ret) {
13291341 spin_lock(&ci->i_ceph_lock);
13301342 ci->i_inline_version = CEPH_INLINE_NONE;
1331− dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR);
1343+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR,
1344+ &prealloc_cf);
13321345 spin_unlock(&ci->i_ceph_lock);
13331346 if (dirty)
13341347 __mark_inode_dirty(inode, dirty);
@@ -1337,6 +1350,7 @@ static long ceph_fallocate(struct file *file, int mode, · 2 unmodified lines
13371350 ceph_put_cap_refs(ci, got);
13381351 unlock:
13391352 mutex_unlock(&inode->i_mutex);
1353+ ceph_free_cap_flush(prealloc_cf);
13401354 return ret;
13411355 }
13421356
fs/ceph/inode.c+13−2
@@ -416,6 +416,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) · 415 unmodified lines
416416 ci->i_flushing_caps = 0;
417417 INIT_LIST_HEAD(&ci->i_dirty_item);
418418 INIT_LIST_HEAD(&ci->i_flushing_item);
419+ ci->i_prealloc_cap_flush = NULL;
419420 ci->i_cap_flush_tree = RB_ROOT;
420421 init_waitqueue_head(&ci->i_cap_wq);
421422 ci->i_hold_caps_min = 0;
@@ -1720,6 +1721,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) · 1298 unmodified lines
17201721 const unsigned int ia_valid = attr->ia_valid;
17211722 struct ceph_mds_request *req;
17221723 struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc;
1724+ struct ceph_cap_flush *prealloc_cf;
17231725 int issued;
17241726 int release = 0, dirtied = 0;
17251727 int mask = 0;
@@ -1734,10 +1736,16 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) · 8 unmodified lines
17341736 if (err != 0)
17351737 return err;
17361738
1739+ prealloc_cf = ceph_alloc_cap_flush();
1740+ if (!prealloc_cf)
1741+ return -ENOMEM;
1742+
17371743 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SETATTR,
17381744 USE_AUTH_MDS);
1739− if (IS_ERR(req))
1745+ if (IS_ERR(req)) {
1746+ ceph_free_cap_flush(prealloc_cf);
17401747 return PTR_ERR(req);
1748+ }
17411749
17421750 spin_lock(&ci->i_ceph_lock);
17431751 issued = __ceph_caps_issued(ci, NULL);
@@ -1895,7 +1903,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) · 151 unmodified lines
18951903 dout("setattr %p ATTR_FILE ... hrm!\n", inode);
18961904
18971905 if (dirtied) {
1898− inode_dirty_flags = __ceph_mark_dirty_caps(ci, dirtied);
1906+ inode_dirty_flags = __ceph_mark_dirty_caps(ci, dirtied,
1907+ &prealloc_cf);
18991908 inode->i_ctime = CURRENT_TIME;
19001909 }
19011910
@@ -1927,9 +1936,11 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) · 25 unmodified lines
19271936 ceph_mdsc_put_request(req);
19281937 if (mask & CEPH_SETATTR_SIZE)
19291938 __ceph_do_pending_vmtruncate(inode);
1939+ ceph_free_cap_flush(prealloc_cf);
19301940 return err;
19311941 out_put:
19321942 ceph_mdsc_put_request(req);
1943+ ceph_free_cap_flush(prealloc_cf);
19331944 return err;
19341945 }
19351946
fs/ceph/mds_client.c+5−1
@@ -1189,6 +1189,10 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, · 1188 unmodified lines
11891189 }
11901190 spin_unlock(&mdsc->cap_dirty_lock);
11911191
1192+ if (!ci->i_dirty_caps && ci->i_prealloc_cap_flush) {
1193+ list_add(&ci->i_prealloc_cap_flush->list, &to_remove);
1194+ ci->i_prealloc_cap_flush = NULL;
1195+ }
11921196 }
11931197 spin_unlock(&ci->i_ceph_lock);
11941198 while (!list_empty(&to_remove)) {
@@ -1196,7 +1200,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, · 1 unmodified line
11961200 cf = list_first_entry(&to_remove,
11971201 struct ceph_cap_flush, list);
11981202 list_del(&cf->list);
1199− kfree(cf);
1203+ ceph_free_cap_flush(cf);
12001204 }
12011205 while (drop--)
12021206 iput(inode);
fs/ceph/super.c+8−0
@@ -622,6 +622,7 @@ static void destroy_fs_client(struct ceph_fs_client *fsc) · 621 unmodified lines
622622 */
623623 struct kmem_cache *ceph_inode_cachep;
624624 struct kmem_cache *ceph_cap_cachep;
625+struct kmem_cache *ceph_cap_flush_cachep;
625626 struct kmem_cache *ceph_dentry_cachep;
626627 struct kmem_cache *ceph_file_cachep;
627628
@@ -647,6 +648,10 @@ static int __init init_caches(void) · 19 unmodified lines
647648 SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD);
648649 if (ceph_cap_cachep == NULL)
649650 goto bad_cap;
651+ ceph_cap_flush_cachep = KMEM_CACHE(ceph_cap_flush,
652+ SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD);
653+ if (ceph_cap_flush_cachep == NULL)
654+ goto bad_cap_flush;
650655
651656 ceph_dentry_cachep = KMEM_CACHE(ceph_dentry_info,
652657 SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD);
@@ -665,6 +670,8 @@ static int __init init_caches(void) · 12 unmodified lines
665670 bad_file:
666671 kmem_cache_destroy(ceph_dentry_cachep);
667672 bad_dentry:
673+ kmem_cache_destroy(ceph_cap_flush_cachep);
674+bad_cap_flush:
668675 kmem_cache_destroy(ceph_cap_cachep);
669676 bad_cap:
670677 kmem_cache_destroy(ceph_inode_cachep);
@@ -681,6 +688,7 @@ static void destroy_caches(void) · 10 unmodified lines
681688
682689 kmem_cache_destroy(ceph_inode_cachep);
683690 kmem_cache_destroy(ceph_cap_cachep);
691+ kmem_cache_destroy(ceph_cap_flush_cachep);
684692 kmem_cache_destroy(ceph_dentry_cachep);
685693 kmem_cache_destroy(ceph_file_cachep);
686694
fs/ceph/super.h+5−1
@@ -309,6 +309,7 @@ struct ceph_inode_info { · 308 unmodified lines
309309 /* we need to track cap writeback on a per-cap-bit basis, to allow
310310 * overlapping, pipelined cap flushes to the mds. we can probably
311311 * reduce the tid to 8 bits if we're concerned about inode size. */
312+ struct ceph_cap_flush *i_prealloc_cap_flush;
312313 struct rb_root i_cap_flush_tree;
313314 wait_queue_head_t i_cap_wq; /* threads waiting on a capability */
314315 unsigned long i_hold_caps_min; /* jiffies */
@@ -578,7 +579,10 @@ static inline int __ceph_caps_dirty(struct ceph_inode_info *ci) · 263 unmodified lines
578579 {
579580 return ci->i_dirty_caps | ci->i_flushing_caps;
580581 }
581−extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask);
582+extern struct ceph_cap_flush *ceph_alloc_cap_flush(void);
583+extern void ceph_free_cap_flush(struct ceph_cap_flush *cf);
584+extern int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask,
585+ struct ceph_cap_flush **pcf);
582586
583587 extern int __ceph_caps_revoking_other(struct ceph_inode_info *ci,
584588 struct ceph_cap *ocap, int mask);
fs/ceph/xattr.c+18−2
@@ -912,6 +912,7 @@ int __ceph_setxattr(struct dentry *dentry, const char *name, · 911 unmodified lines
912912 struct ceph_vxattr *vxattr;
913913 struct ceph_inode_info *ci = ceph_inode(inode);
914914 struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc;
915+ struct ceph_cap_flush *prealloc_cf = NULL;
915916 int issued;
916917 int err;
917918 int dirty = 0;
@@ -950,6 +951,10 @@ int __ceph_setxattr(struct dentry *dentry, const char *name, · 32 unmodified lines
950951 if (!xattr)
951952 goto out;
952953
954+ prealloc_cf = ceph_alloc_cap_flush();
955+ if (!prealloc_cf)
956+ goto out;
957+
953958 spin_lock(&ci->i_ceph_lock);
954959 retry:
955960 issued = __ceph_caps_issued(ci, NULL);
@@ -991,7 +996,8 @@ retry: · 35 unmodified lines
991996 flags, value ? 1 : -1, &xattr);
992997
993998 if (!err) {
994− dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
999+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL,
1000+ &prealloc_cf);
9951001 ci->i_xattrs.dirty = true;
9961002 inode->i_ctime = CURRENT_TIME;
9971003 }
@@ -1001,6 +1007,7 @@ retry: · 3 unmodified lines
10011007 up_read(&mdsc->snap_rwsem);
10021008 if (dirty)
10031009 __mark_inode_dirty(inode, dirty);
1010+ ceph_free_cap_flush(prealloc_cf);
10041011 return err;
10051012
10061013 do_sync:
@@ -1010,6 +1017,7 @@ do_sync_unlocked: · 3 unmodified lines
10101017 up_read(&mdsc->snap_rwsem);
10111018 err = ceph_sync_setxattr(dentry, name, value, size, flags);
10121019 out:
1020+ ceph_free_cap_flush(prealloc_cf);
10131021 kfree(newname);
10141022 kfree(newval);
10151023 kfree(xattr);
@@ -1062,6 +1070,7 @@ int __ceph_removexattr(struct dentry *dentry, const char *name) · 46 unmodified lines
10621070 struct ceph_vxattr *vxattr;
10631071 struct ceph_inode_info *ci = ceph_inode(inode);
10641072 struct ceph_mds_client *mdsc = ceph_sb_to_client(dentry->d_sb)->mdsc;
1073+ struct ceph_cap_flush *prealloc_cf = NULL;
10651074 int issued;
10661075 int err;
10671076 int required_blob_size;
@@ -1079,6 +1088,10 @@ int __ceph_removexattr(struct dentry *dentry, const char *name) · 11 unmodified lines
10791088 if (!strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN))
10801089 goto do_sync_unlocked;
10811090
1091+ prealloc_cf = ceph_alloc_cap_flush();
1092+ if (!prealloc_cf)
1093+ return -ENOMEM;
1094+
10821095 err = -ENOMEM;
10831096 spin_lock(&ci->i_ceph_lock);
10841097 retry:
@@ -1120,7 +1133,8 @@ retry: · 35 unmodified lines
11201133
11211134 err = __remove_xattr_by_name(ceph_inode(inode), name);
11221135
1123− dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL);
1136+ dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_XATTR_EXCL,
1137+ &prealloc_cf);
11241138 ci->i_xattrs.dirty = true;
11251139 inode->i_ctime = CURRENT_TIME;
11261140 spin_unlock(&ci->i_ceph_lock);
@@ -1128,12 +1142,14 @@ retry: · 1 unmodified line
11281142 up_read(&mdsc->snap_rwsem);
11291143 if (dirty)
11301144 __mark_inode_dirty(inode, dirty);
1145+ ceph_free_cap_flush(prealloc_cf);
11311146 return err;
11321147 do_sync:
11331148 spin_unlock(&ci->i_ceph_lock);
11341149 do_sync_unlocked:
11351150 if (lock_snap_rwsem)
11361151 up_read(&mdsc->snap_rwsem);
1152+ ceph_free_cap_flush(prealloc_cf);
11371153 err = ceph_send_removexattr(dentry, name);
11381154 return err;
11391155 }
include/linux/ceph/libceph.h+1−0
@@ -174,6 +174,7 @@ static inline int calc_pages_for(u64 off, u64 len) · 173 unmodified lines
174174
175175 extern struct kmem_cache *ceph_inode_cachep;
176176 extern struct kmem_cache *ceph_cap_cachep;
177+extern struct kmem_cache *ceph_cap_flush_cachep;
177178 extern struct kmem_cache *ceph_dentry_cachep;
178179 extern struct kmem_cache *ceph_file_cachep;
179180

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t