History

ublk: enable zoned storage support

Add zoned storage support to ublk: report_zones and operations: - REQ_OP_ZONE_OPEN - REQ_OP_ZONE_CLOSE - REQ_OP_ZONE_FINISH - REQ_OP_ZONE_RESET - REQ_OP_ZONE_APPEND The zone append feature uses the `addr` field of `struct ublksrv_io_cmd` to communicate ALBA back to the kernel. Therefore ublk must be used with the user copy feature (UBLK_F_USER_COPY) for zoned storage support to be available. Without this feature, ublk will not allow zoned storage support.

Andreas Hindborg committed 3 years ago

Commit 29802d71 parent 1a6e88b2 files · +370 −24Browse files

Unified · Split

2 files
  1. drivers/block/ublk_drv.c +316 −15
  2. include/uapi/linux/ublk_cmd.h +54 −9
drivers/block/ublk_drv.c+316−15
@@ -56,16 +56,21 @@ · 55 unmodified lines
5656 | UBLK_F_USER_RECOVERY_REISSUE \
5757 | UBLK_F_UNPRIVILEGED_DEV \
5858 | UBLK_F_CMD_IOCTL_ENCODE \
59− | UBLK_F_USER_COPY)
59+ | UBLK_F_USER_COPY \
60+ | UBLK_F_ZONED)
6061
6162 /* All UBLK_PARAM_TYPE_* should be included here */
62−#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | \
63− UBLK_PARAM_TYPE_DISCARD | UBLK_PARAM_TYPE_DEVT)
63+#define UBLK_PARAM_TYPE_ALL \
64+ (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD | \
65+ UBLK_PARAM_TYPE_DEVT | UBLK_PARAM_TYPE_ZONED)
6466
6567 struct ublk_rq_data {
6668 struct llist_node node;
6769
6870 struct kref ref;
71+ __u64 sector;
72+ __u32 operation;
73+ __u32 nr_zones;
6974 };
7075
7176 struct ublk_uring_cmd_pdu {
@@ -185,11 +190,263 @@ struct ublk_params_header { · 113 unmodified lines
185190 __u32 types;
186191 };
187192
193+static inline unsigned int ublk_req_build_flags(struct request *req);
194+static inline struct ublksrv_io_desc *ublk_get_iod(struct ublk_queue *ubq,
195+ int tag);
196+
188197 static inline bool ublk_dev_is_user_copy(const struct ublk_device *ub)
189198 {
190199 return ub->dev_info.flags & UBLK_F_USER_COPY;
191200 }
192201
202+static inline bool ublk_dev_is_zoned(const struct ublk_device *ub)
203+{
204+ return ub->dev_info.flags & UBLK_F_ZONED;
205+}
206+
207+static inline bool ublk_queue_is_zoned(struct ublk_queue *ubq)
208+{
209+ return ubq->flags & UBLK_F_ZONED;
210+}
211+
212+#ifdef CONFIG_BLK_DEV_ZONED
213+
214+static int ublk_get_nr_zones(const struct ublk_device *ub)
215+{
216+ const struct ublk_param_basic *p = &ub->params.basic;
217+
218+ /* Zone size is a power of 2 */
219+ return p->dev_sectors >> ilog2(p->chunk_sectors);
220+}
221+
222+static int ublk_revalidate_disk_zones(struct ublk_device *ub)
223+{
224+ return blk_revalidate_disk_zones(ub->ub_disk, NULL);
225+}
226+
227+static int ublk_dev_param_zoned_validate(const struct ublk_device *ub)
228+{
229+ const struct ublk_param_zoned *p = &ub->params.zoned;
230+ int nr_zones;
231+
232+ if (!ublk_dev_is_zoned(ub))
233+ return -EINVAL;
234+
235+ if (!p->max_zone_append_sectors)
236+ return -EINVAL;
237+
238+ nr_zones = ublk_get_nr_zones(ub);
239+
240+ if (p->max_active_zones > nr_zones)
241+ return -EINVAL;
242+
243+ if (p->max_open_zones > nr_zones)
244+ return -EINVAL;
245+
246+ return 0;
247+}
248+
249+static int ublk_dev_param_zoned_apply(struct ublk_device *ub)
250+{
251+ const struct ublk_param_zoned *p = &ub->params.zoned;
252+
253+ disk_set_zoned(ub->ub_disk, BLK_ZONED_HM);
254+ blk_queue_required_elevator_features(ub->ub_disk->queue,
255+ ELEVATOR_F_ZBD_SEQ_WRITE);
256+ disk_set_max_active_zones(ub->ub_disk, p->max_active_zones);
257+ disk_set_max_open_zones(ub->ub_disk, p->max_open_zones);
258+ blk_queue_max_zone_append_sectors(ub->ub_disk->queue, p->max_zone_append_sectors);
259+
260+ ub->ub_disk->nr_zones = ublk_get_nr_zones(ub);
261+
262+ return 0;
263+}
264+
265+/* Based on virtblk_alloc_report_buffer */
266+static void *ublk_alloc_report_buffer(struct ublk_device *ublk,
267+ unsigned int nr_zones, size_t *buflen)
268+{
269+ struct request_queue *q = ublk->ub_disk->queue;
270+ size_t bufsize;
271+ void *buf;
272+
273+ nr_zones = min_t(unsigned int, nr_zones,
274+ ublk->ub_disk->nr_zones);
275+
276+ bufsize = nr_zones * sizeof(struct blk_zone);
277+ bufsize =
278+ min_t(size_t, bufsize, queue_max_hw_sectors(q) << SECTOR_SHIFT);
279+
280+ while (bufsize >= sizeof(struct blk_zone)) {
281+ buf = kvmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY);
282+ if (buf) {
283+ *buflen = bufsize;
284+ return buf;
285+ }
286+ bufsize >>= 1;
287+ }
288+
289+ *buflen = 0;
290+ return NULL;
291+}
292+
293+static int ublk_report_zones(struct gendisk *disk, sector_t sector,
294+ unsigned int nr_zones, report_zones_cb cb, void *data)
295+{
296+ struct ublk_device *ub = disk->private_data;
297+ unsigned int zone_size_sectors = disk->queue->limits.chunk_sectors;
298+ unsigned int first_zone = sector >> ilog2(zone_size_sectors);
299+ unsigned int done_zones = 0;
300+ unsigned int max_zones_per_request;
301+ int ret;
302+ struct blk_zone *buffer;
303+ size_t buffer_length;
304+
305+ nr_zones = min_t(unsigned int, ub->ub_disk->nr_zones - first_zone,
306+ nr_zones);
307+
308+ buffer = ublk_alloc_report_buffer(ub, nr_zones, &buffer_length);
309+ if (!buffer)
310+ return -ENOMEM;
311+
312+ max_zones_per_request = buffer_length / sizeof(struct blk_zone);
313+
314+ while (done_zones < nr_zones) {
315+ unsigned int remaining_zones = nr_zones - done_zones;
316+ unsigned int zones_in_request =
317+ min_t(unsigned int, remaining_zones, max_zones_per_request);
318+ struct request *req;
319+ struct ublk_rq_data *pdu;
320+ blk_status_t status;
321+
322+ memset(buffer, 0, buffer_length);
323+
324+ req = blk_mq_alloc_request(disk->queue, REQ_OP_DRV_IN, 0);
325+ if (IS_ERR(req)) {
326+ ret = PTR_ERR(req);
327+ goto out;
328+ }
329+
330+ pdu = blk_mq_rq_to_pdu(req);
331+ pdu->operation = UBLK_IO_OP_REPORT_ZONES;
332+ pdu->sector = sector;
333+ pdu->nr_zones = zones_in_request;
334+
335+ ret = blk_rq_map_kern(disk->queue, req, buffer, buffer_length,
336+ GFP_KERNEL);
337+ if (ret) {
338+ blk_mq_free_request(req);
339+ goto out;
340+ }
341+
342+ status = blk_execute_rq(req, 0);
343+ ret = blk_status_to_errno(status);
344+ blk_mq_free_request(req);
345+ if (ret)
346+ goto out;
347+
348+ for (unsigned int i = 0; i < zones_in_request; i++) {
349+ struct blk_zone *zone = buffer + i;
350+
351+ /* A zero length zone means no more zones in this response */
352+ if (!zone->len)
353+ break;
354+
355+ ret = cb(zone, i, data);
356+ if (ret)
357+ goto out;
358+
359+ done_zones++;
360+ sector += zone_size_sectors;
361+
362+ }
363+ }
364+
365+ ret = done_zones;
366+
367+out:
368+ kvfree(buffer);
369+ return ret;
370+}
371+
372+static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
373+ struct request *req)
374+{
375+ struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag);
376+ struct ublk_io *io = &ubq->ios[req->tag];
377+ struct ublk_rq_data *pdu = blk_mq_rq_to_pdu(req);
378+ u32 ublk_op;
379+
380+ switch (req_op(req)) {
381+ case REQ_OP_ZONE_OPEN:
382+ ublk_op = UBLK_IO_OP_ZONE_OPEN;
383+ break;
384+ case REQ_OP_ZONE_CLOSE:
385+ ublk_op = UBLK_IO_OP_ZONE_CLOSE;
386+ break;
387+ case REQ_OP_ZONE_FINISH:
388+ ublk_op = UBLK_IO_OP_ZONE_FINISH;
389+ break;
390+ case REQ_OP_ZONE_RESET:
391+ ublk_op = UBLK_IO_OP_ZONE_RESET;
392+ break;
393+ case REQ_OP_ZONE_APPEND:
394+ ublk_op = UBLK_IO_OP_ZONE_APPEND;
395+ break;
396+ case REQ_OP_DRV_IN:
397+ ublk_op = pdu->operation;
398+ switch (ublk_op) {
399+ case UBLK_IO_OP_REPORT_ZONES:
400+ iod->op_flags = ublk_op | ublk_req_build_flags(req);
401+ iod->nr_zones = pdu->nr_zones;
402+ iod->start_sector = pdu->sector;
403+ return BLK_STS_OK;
404+ default:
405+ return BLK_STS_IOERR;
406+ }
407+ case REQ_OP_ZONE_RESET_ALL:
408+ case REQ_OP_DRV_OUT:
409+ /* We do not support reset_all and drv_out */
410+ return BLK_STS_NOTSUPP;
411+ default:
412+ return BLK_STS_IOERR;
413+ }
414+
415+ iod->op_flags = ublk_op | ublk_req_build_flags(req);
416+ iod->nr_sectors = blk_rq_sectors(req);
417+ iod->start_sector = blk_rq_pos(req);
418+ iod->addr = io->addr;
419+
420+ return BLK_STS_OK;
421+}
422+
423+#else
424+
425+#define ublk_report_zones (NULL)
426+
427+static int ublk_dev_param_zoned_validate(const struct ublk_device *ub)
428+{
429+ return -EOPNOTSUPP;
430+}
431+
432+static int ublk_dev_param_zoned_apply(struct ublk_device *ub)
433+{
434+ return -EOPNOTSUPP;
435+}
436+
437+static int ublk_revalidate_disk_zones(struct ublk_device *ub)
438+{
439+ return 0;
440+}
441+
442+static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
443+ struct request *req)
444+{
445+ return -EOPNOTSUPP;
446+}
447+
448+#endif
449+
193450 static inline void __ublk_complete_rq(struct request *req);
194451 static void ublk_complete_rq(struct kref *ref);
195452
@@ -286,6 +543,9 @@ static int ublk_validate_params(const struct ublk_device *ub) · 90 unmodified lines
286543
287544 if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9))
288545 return -EINVAL;
546+
547+ if (ublk_dev_is_zoned(ub) && !p->chunk_sectors)
548+ return -EINVAL;
289549 } else
290550 return -EINVAL;
291551
@@ -304,6 +564,11 @@ static int ublk_validate_params(const struct ublk_device *ub) · 12 unmodified lines
304564 if (ub->params.types & UBLK_PARAM_TYPE_DEVT)
305565 return -EINVAL;
306566
567+ if (ub->params.types & UBLK_PARAM_TYPE_ZONED)
568+ return ublk_dev_param_zoned_validate(ub);
569+ else if (ublk_dev_is_zoned(ub))
570+ return -EINVAL;
571+
307572 return 0;
308573 }
309574
@@ -317,6 +582,9 @@ static int ublk_apply_params(struct ublk_device *ub) · 7 unmodified lines
317582 if (ub->params.types & UBLK_PARAM_TYPE_DISCARD)
318583 ublk_dev_param_discard_apply(ub);
319584
585+ if (ub->params.types & UBLK_PARAM_TYPE_ZONED)
586+ return ublk_dev_param_zoned_apply(ub);
587+
320588 return 0;
321589 }
322590
@@ -487,6 +755,7 @@ static const struct block_device_operations ub_fops = { · 164 unmodified lines
487755 .owner = THIS_MODULE,
488756 .open = ublk_open,
489757 .free_disk = ublk_free_disk,
758+ .report_zones = ublk_report_zones,
490759 };
491760
492761 #define UBLK_MAX_PIN_PAGES 32
@@ -601,7 +870,8 @@ static inline bool ublk_need_map_req(const struct request *req) · 108 unmodified lines
601870
602871 static inline bool ublk_need_unmap_req(const struct request *req)
603872 {
604− return ublk_rq_has_data(req) && req_op(req) == REQ_OP_READ;
873+ return ublk_rq_has_data(req) &&
874+ (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN);
605875 }
606876
607877 static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req,
@@ -685,8 +955,13 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) · 77 unmodified lines
685955 {
686956 struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag);
687957 struct ublk_io *io = &ubq->ios[req->tag];
958+ enum req_op op = req_op(req);
688959 u32 ublk_op;
689960
961+ if (!ublk_queue_is_zoned(ubq) &&
962+ (op_is_zone_mgmt(op) || op == REQ_OP_ZONE_APPEND))
963+ return -EIO;
964+
690965 switch (req_op(req)) {
691966 case REQ_OP_READ:
692967 ublk_op = UBLK_IO_OP_READ;
@@ -704,6 +979,8 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req) · 11 unmodified lines
704979 ublk_op = UBLK_IO_OP_WRITE_ZEROES;
705980 break;
706981 default:
982+ if (ublk_queue_is_zoned(ubq))
983+ return ublk_setup_iod_zoned(ubq, req);
707984 return BLK_STS_IOERR;
708985 }
709986
@@ -756,7 +1033,8 @@ static inline void __ublk_complete_rq(struct request *req) · 46 unmodified lines
7561033 *
7571034 * Both the two needn't unmap.
7581035 */
759− if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE)
1036+ if (req_op(req) != REQ_OP_READ && req_op(req) != REQ_OP_WRITE &&
1037+ req_op(req) != REQ_OP_DRV_IN)
7601038 goto exit;
7611039
7621040 /* for READ request, writing data in iod->addr to rq buffers */
@@ -1120,6 +1398,9 @@ static void ublk_commit_completion(struct ublk_device *ub, · 357 unmodified lines
11201398 /* find the io request and complete */
11211399 req = blk_mq_tag_to_rq(ub->tag_set.tags[qid], tag);
11221400
1401+ if (req_op(req) == REQ_OP_ZONE_APPEND)
1402+ req->__sector = ub_cmd->zone_append_lba;
1403+
11231404 if (req && likely(!blk_should_fake_timeout(req->q)))
11241405 ublk_put_req_ref(ubq, req);
11251406 }
@@ -1468,8 +1749,11 @@ static int __ublk_ch_uring_cmd(struct io_uring_cmd *cmd, · 342 unmodified lines
14681749 if (!ub_cmd->addr && (!ublk_need_get_data(ubq) ||
14691750 req_op(req) == REQ_OP_READ))
14701751 goto out;
1471− } else if (ub_cmd->addr) {
1472− /* User copy requires addr to be unset */
1752+ } else if (req_op(req) != REQ_OP_ZONE_APPEND && ub_cmd->addr) {
1753+ /*
1754+ * User copy requires addr to be unset when command is
1755+ * not zone append
1756+ */
14731757 ret = -EINVAL;
14741758 goto out;
14751759 }
@@ -1546,11 +1830,14 @@ static inline bool ublk_check_ubuf_dir(const struct request *req, · 70 unmodified lines
15461830 int ubuf_dir)
15471831 {
15481832 /* copy ubuf to request pages */
1549− if (req_op(req) == REQ_OP_READ && ubuf_dir == ITER_SOURCE)
1833+ if ((req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN) &&
1834+ ubuf_dir == ITER_SOURCE)
15501835 return true;
15511836
15521837 /* copy request pages to ubuf */
1553− if (req_op(req) == REQ_OP_WRITE && ubuf_dir == ITER_DEST)
1838+ if ((req_op(req) == REQ_OP_WRITE ||
1839+ req_op(req) == REQ_OP_ZONE_APPEND) &&
1840+ ubuf_dir == ITER_DEST)
15541841 return true;
15551842
15561843 return false;
@@ -1889,17 +2176,24 @@ static int ublk_ctrl_start_dev(struct ublk_device *ub, struct io_uring_cmd *cmd) · 332 unmodified lines
18892176
18902177 get_device(&ub->cdev_dev);
18912178 ub->dev_info.state = UBLK_S_DEV_LIVE;
2179+
2180+ if (ublk_dev_is_zoned(ub)) {
2181+ ret = ublk_revalidate_disk_zones(ub);
2182+ if (ret)
2183+ goto out_put_cdev;
2184+ }
2185+
18922186 ret = add_disk(disk);
2187+ if (ret)
2188+ goto out_put_cdev;
2189+
2190+ set_bit(UB_STATE_USED, &ub->state);
2191+
2192+out_put_cdev:
18932193 if (ret) {
1894− /*
1895− * Has to drop the reference since ->free_disk won't be
1896− * called in case of add_disk failure.
1897− */
18982194 ub->dev_info.state = UBLK_S_DEV_DEAD;
18992195 ublk_put_device(ub);
1900− goto out_put_disk;
19012196 }
1902− set_bit(UB_STATE_USED, &ub->state);
19032197 out_put_disk:
19042198 if (ret)
19052199 put_disk(disk);
@@ -2049,6 +2343,13 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd) · 143 unmodified lines
20492343 if (ublk_dev_is_user_copy(ub))
20502344 ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
20512345
2346+ /* Zoned storage support requires user copy feature */
2347+ if (ublk_dev_is_zoned(ub) &&
2348+ (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !ublk_dev_is_user_copy(ub))) {
2349+ ret = -EINVAL;
2350+ goto out_free_dev_number;
2351+ }
2352+
20522353 /* We are not ready to support zero copy */
20532354 ub->dev_info.flags &= ~UBLK_F_SUPPORT_ZERO_COPY;
20542355
include/uapi/linux/ublk_cmd.h+54−9
@@ -176,6 +176,12 @@ · 175 unmodified lines
176176 /* Copy between request and user buffer by pread()/pwrite() */
177177 #define UBLK_F_USER_COPY (1UL << 7)
178178
179+/*
180+ * User space sets this flag when setting up the device to request zoned storage support. Kernel may
181+ * deny the request by returning an error.
182+ */
183+#define UBLK_F_ZONED (1ULL << 8)
184+
179185 /* device state */
180186 #define UBLK_S_DEV_DEAD 0
181187 #define UBLK_S_DEV_LIVE 1
@@ -232,9 +238,26 @@ struct ublksrv_ctrl_dev_info { · 50 unmodified lines
232238 #define UBLK_IO_OP_READ 0
233239 #define UBLK_IO_OP_WRITE 1
234240 #define UBLK_IO_OP_FLUSH 2
235−#define UBLK_IO_OP_DISCARD 3
236−#define UBLK_IO_OP_WRITE_SAME 4
237−#define UBLK_IO_OP_WRITE_ZEROES 5
241+#define UBLK_IO_OP_DISCARD 3
242+#define UBLK_IO_OP_WRITE_SAME 4
243+#define UBLK_IO_OP_WRITE_ZEROES 5
244+#define UBLK_IO_OP_ZONE_OPEN 10
245+#define UBLK_IO_OP_ZONE_CLOSE 11
246+#define UBLK_IO_OP_ZONE_FINISH 12
247+#define UBLK_IO_OP_ZONE_APPEND 13
248+#define UBLK_IO_OP_ZONE_RESET 15
249+/*
250+ * Construct a zone report. The report request is carried in `struct
251+ * ublksrv_io_desc`. The `start_sector` field must be the first sector of a zone
252+ * and shall indicate the first zone of the report. The `nr_zones` shall
253+ * indicate how many zones should be reported at most. The report shall be
254+ * delivered as a `struct blk_zone` array. To report fewer zones than requested,
255+ * zero the last entry of the returned array.
256+ *
257+ * Related definitions(blk_zone, blk_zone_cond, blk_zone_type, ...) in
258+ * include/uapi/linux/blkzoned.h are part of ublk UAPI.
259+ */
260+#define UBLK_IO_OP_REPORT_ZONES 18
238261
239262 #define UBLK_IO_F_FAILFAST_DEV (1U << 8)
240263 #define UBLK_IO_F_FAILFAST_TRANSPORT (1U << 9)
@@ -255,7 +278,10 @@ struct ublksrv_io_desc { · 14 unmodified lines
255278 /* op: bit 0-7, flags: bit 8-31 */
256279 __u32 op_flags;
257280
258− __u32 nr_sectors;
281+ union {
282+ __u32 nr_sectors;
283+ __u32 nr_zones; /* for UBLK_IO_OP_REPORT_ZONES */
284+ };
259285
260286 /* start sector for this io */
261287 __u64 start_sector;
@@ -284,11 +310,21 @@ struct ublksrv_io_cmd { · 22 unmodified lines
284310 /* io result, it is valid for COMMIT* command only */
285311 __s32 result;
286312
287− /*
288− * userspace buffer address in ublksrv daemon process, valid for
289− * FETCH* command only
290− */
291− __u64 addr;
313+ union {
314+ /*
315+ * userspace buffer address in ublksrv daemon process, valid for
316+ * FETCH* command only
317+ *
318+ * `addr` should not be used when UBLK_F_USER_COPY is enabled,
319+ * because userspace handles data copy by pread()/pwrite() over
320+ * /dev/ublkcN. But in case of UBLK_F_ZONED, this union is
321+ * re-used to pass back the allocated LBA for
322+ * UBLK_IO_OP_ZONE_APPEND which actually depends on
323+ * UBLK_F_USER_COPY
324+ */
325+ __u64 addr;
326+ __u64 zone_append_lba;
327+ };
292328 };
293329
294330 struct ublk_param_basic {
@@ -331,6 +367,13 @@ struct ublk_param_devt { · 36 unmodified lines
331367 __u32 disk_minor;
332368 };
333369
370+struct ublk_param_zoned {
371+ __u32 max_open_zones;
372+ __u32 max_active_zones;
373+ __u32 max_zone_append_sectors;
374+ __u8 reserved[20];
375+};
376+
334377 struct ublk_params {
335378 /*
336379 * Total length of parameters, userspace has to set 'len' for both
@@ -342,11 +385,13 @@ struct ublk_params { · 5 unmodified lines
342385 #define UBLK_PARAM_TYPE_BASIC (1 << 0)
343386 #define UBLK_PARAM_TYPE_DISCARD (1 << 1)
344387 #define UBLK_PARAM_TYPE_DEVT (1 << 2)
388+#define UBLK_PARAM_TYPE_ZONED (1 << 3)
345389 __u32 types; /* types of parameter included */
346390
347391 struct ublk_param_basic basic;
348392 struct ublk_param_discard discard;
349393 struct ublk_param_devt devt;
394+ struct ublk_param_zoned zoned;
350395 };
351396
352397 #endif

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