-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnvme-scsi.c
3070 lines (2735 loc) · 84.1 KB
/
nvme-scsi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* NVM Express device driver
* Copyright (c) 2011-2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
/*
* Refer to the SCSI-NVMe Translation spec for details on how
* each command is translated.
*/
#include <linux/nvme.h>
#include <linux/bio.h>
#include <linux/bitops.h>
#include <linux/blkdev.h>
#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kdev_t.h>
#include <linux/kthread.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/poison.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <scsi/sg.h>
#include <scsi/scsi.h>
static int sg_version_num = 30534; /* 2 digits for each component */
#define SNTI_TRANSLATION_SUCCESS 0
#define SNTI_INTERNAL_ERROR 1
/* VPD Page Codes */
#define VPD_SUPPORTED_PAGES 0x00
#define VPD_SERIAL_NUMBER 0x80
#define VPD_DEVICE_IDENTIFIERS 0x83
#define VPD_EXTENDED_INQUIRY 0x86
#define VPD_BLOCK_LIMITS 0xB0
#define VPD_BLOCK_DEV_CHARACTERISTICS 0xB1
/* CDB offsets */
#define REPORT_LUNS_CDB_ALLOC_LENGTH_OFFSET 6
#define REPORT_LUNS_SR_OFFSET 2
#define READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET 10
#define REQUEST_SENSE_CDB_ALLOC_LENGTH_OFFSET 4
#define REQUEST_SENSE_DESC_OFFSET 1
#define REQUEST_SENSE_DESC_MASK 0x01
#define DESCRIPTOR_FORMAT_SENSE_DATA_TYPE 1
#define INQUIRY_EVPD_BYTE_OFFSET 1
#define INQUIRY_PAGE_CODE_BYTE_OFFSET 2
#define INQUIRY_EVPD_BIT_MASK 1
#define INQUIRY_CDB_ALLOCATION_LENGTH_OFFSET 3
#define START_STOP_UNIT_CDB_IMMED_OFFSET 1
#define START_STOP_UNIT_CDB_IMMED_MASK 0x1
#define START_STOP_UNIT_CDB_POWER_COND_MOD_OFFSET 3
#define START_STOP_UNIT_CDB_POWER_COND_MOD_MASK 0xF
#define START_STOP_UNIT_CDB_POWER_COND_OFFSET 4
#define START_STOP_UNIT_CDB_POWER_COND_MASK 0xF0
#define START_STOP_UNIT_CDB_NO_FLUSH_OFFSET 4
#define START_STOP_UNIT_CDB_NO_FLUSH_MASK 0x4
#define START_STOP_UNIT_CDB_START_OFFSET 4
#define START_STOP_UNIT_CDB_START_MASK 0x1
#define WRITE_BUFFER_CDB_MODE_OFFSET 1
#define WRITE_BUFFER_CDB_MODE_MASK 0x1F
#define WRITE_BUFFER_CDB_BUFFER_ID_OFFSET 2
#define WRITE_BUFFER_CDB_BUFFER_OFFSET_OFFSET 3
#define WRITE_BUFFER_CDB_PARM_LIST_LENGTH_OFFSET 6
#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_OFFSET 1
#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_MASK 0xC0
#define FORMAT_UNIT_CDB_FORMAT_PROT_INFO_SHIFT 6
#define FORMAT_UNIT_CDB_LONG_LIST_OFFSET 1
#define FORMAT_UNIT_CDB_LONG_LIST_MASK 0x20
#define FORMAT_UNIT_CDB_FORMAT_DATA_OFFSET 1
#define FORMAT_UNIT_CDB_FORMAT_DATA_MASK 0x10
#define FORMAT_UNIT_SHORT_PARM_LIST_LEN 4
#define FORMAT_UNIT_LONG_PARM_LIST_LEN 8
#define FORMAT_UNIT_PROT_INT_OFFSET 3
#define FORMAT_UNIT_PROT_FIELD_USAGE_OFFSET 0
#define FORMAT_UNIT_PROT_FIELD_USAGE_MASK 0x07
#define UNMAP_CDB_PARAM_LIST_LENGTH_OFFSET 7
/* Misc. defines */
#define NIBBLE_SHIFT 4
#define FIXED_SENSE_DATA 0x70
#define DESC_FORMAT_SENSE_DATA 0x72
#define FIXED_SENSE_DATA_ADD_LENGTH 10
#define LUN_ENTRY_SIZE 8
#define LUN_DATA_HEADER_SIZE 8
#define ALL_LUNS_RETURNED 0x02
#define ALL_WELL_KNOWN_LUNS_RETURNED 0x01
#define RESTRICTED_LUNS_RETURNED 0x00
#define NVME_POWER_STATE_START_VALID 0x00
#define NVME_POWER_STATE_ACTIVE 0x01
#define NVME_POWER_STATE_IDLE 0x02
#define NVME_POWER_STATE_STANDBY 0x03
#define NVME_POWER_STATE_LU_CONTROL 0x07
#define POWER_STATE_0 0
#define POWER_STATE_1 1
#define POWER_STATE_2 2
#define POWER_STATE_3 3
#define DOWNLOAD_SAVE_ACTIVATE 0x05
#define DOWNLOAD_SAVE_DEFER_ACTIVATE 0x0E
#define ACTIVATE_DEFERRED_MICROCODE 0x0F
#define FORMAT_UNIT_IMMED_MASK 0x2
#define FORMAT_UNIT_IMMED_OFFSET 1
#define KELVIN_TEMP_FACTOR 273
#define FIXED_FMT_SENSE_DATA_SIZE 18
#define DESC_FMT_SENSE_DATA_SIZE 8
/* SCSI/NVMe defines and bit masks */
#define INQ_STANDARD_INQUIRY_PAGE 0x00
#define INQ_SUPPORTED_VPD_PAGES_PAGE 0x00
#define INQ_UNIT_SERIAL_NUMBER_PAGE 0x80
#define INQ_DEVICE_IDENTIFICATION_PAGE 0x83
#define INQ_EXTENDED_INQUIRY_DATA_PAGE 0x86
#define INQ_BDEV_LIMITS_PAGE 0xB0
#define INQ_BDEV_CHARACTERISTICS_PAGE 0xB1
#define INQ_SERIAL_NUMBER_LENGTH 0x14
#define INQ_NUM_SUPPORTED_VPD_PAGES 6
#define VERSION_SPC_4 0x06
#define ACA_UNSUPPORTED 0
#define STANDARD_INQUIRY_LENGTH 36
#define ADDITIONAL_STD_INQ_LENGTH 31
#define EXTENDED_INQUIRY_DATA_PAGE_LENGTH 0x3C
#define RESERVED_FIELD 0
/* SCSI READ/WRITE Defines */
#define IO_CDB_WP_MASK 0xE0
#define IO_CDB_WP_SHIFT 5
#define IO_CDB_FUA_MASK 0x8
#define IO_6_CDB_LBA_OFFSET 0
#define IO_6_CDB_LBA_MASK 0x001FFFFF
#define IO_6_CDB_TX_LEN_OFFSET 4
#define IO_6_DEFAULT_TX_LEN 256
#define IO_10_CDB_LBA_OFFSET 2
#define IO_10_CDB_TX_LEN_OFFSET 7
#define IO_10_CDB_WP_OFFSET 1
#define IO_10_CDB_FUA_OFFSET 1
#define IO_12_CDB_LBA_OFFSET 2
#define IO_12_CDB_TX_LEN_OFFSET 6
#define IO_12_CDB_WP_OFFSET 1
#define IO_12_CDB_FUA_OFFSET 1
#define IO_16_CDB_FUA_OFFSET 1
#define IO_16_CDB_WP_OFFSET 1
#define IO_16_CDB_LBA_OFFSET 2
#define IO_16_CDB_TX_LEN_OFFSET 10
/* Mode Sense/Select defines */
#define MODE_PAGE_INFO_EXCEP 0x1C
#define MODE_PAGE_CACHING 0x08
#define MODE_PAGE_CONTROL 0x0A
#define MODE_PAGE_POWER_CONDITION 0x1A
#define MODE_PAGE_RETURN_ALL 0x3F
#define MODE_PAGE_BLK_DES_LEN 0x08
#define MODE_PAGE_LLBAA_BLK_DES_LEN 0x10
#define MODE_PAGE_CACHING_LEN 0x14
#define MODE_PAGE_CONTROL_LEN 0x0C
#define MODE_PAGE_POW_CND_LEN 0x28
#define MODE_PAGE_INF_EXC_LEN 0x0C
#define MODE_PAGE_ALL_LEN 0x54
#define MODE_SENSE6_MPH_SIZE 4
#define MODE_SENSE6_ALLOC_LEN_OFFSET 4
#define MODE_SENSE_PAGE_CONTROL_OFFSET 2
#define MODE_SENSE_PAGE_CONTROL_MASK 0xC0
#define MODE_SENSE_PAGE_CODE_OFFSET 2
#define MODE_SENSE_PAGE_CODE_MASK 0x3F
#define MODE_SENSE_LLBAA_OFFSET 1
#define MODE_SENSE_LLBAA_MASK 0x10
#define MODE_SENSE_LLBAA_SHIFT 4
#define MODE_SENSE_DBD_OFFSET 1
#define MODE_SENSE_DBD_MASK 8
#define MODE_SENSE_DBD_SHIFT 3
#define MODE_SENSE10_MPH_SIZE 8
#define MODE_SENSE10_ALLOC_LEN_OFFSET 7
#define MODE_SELECT_CDB_PAGE_FORMAT_OFFSET 1
#define MODE_SELECT_CDB_SAVE_PAGES_OFFSET 1
#define MODE_SELECT_6_CDB_PARAM_LIST_LENGTH_OFFSET 4
#define MODE_SELECT_10_CDB_PARAM_LIST_LENGTH_OFFSET 7
#define MODE_SELECT_CDB_PAGE_FORMAT_MASK 0x10
#define MODE_SELECT_CDB_SAVE_PAGES_MASK 0x1
#define MODE_SELECT_6_BD_OFFSET 3
#define MODE_SELECT_10_BD_OFFSET 6
#define MODE_SELECT_10_LLBAA_OFFSET 4
#define MODE_SELECT_10_LLBAA_MASK 1
#define MODE_SELECT_6_MPH_SIZE 4
#define MODE_SELECT_10_MPH_SIZE 8
#define CACHING_MODE_PAGE_WCE_MASK 0x04
#define MODE_SENSE_BLK_DESC_ENABLED 0
#define MODE_SENSE_BLK_DESC_COUNT 1
#define MODE_SELECT_PAGE_CODE_MASK 0x3F
#define SHORT_DESC_BLOCK 8
#define LONG_DESC_BLOCK 16
#define MODE_PAGE_POW_CND_LEN_FIELD 0x26
#define MODE_PAGE_INF_EXC_LEN_FIELD 0x0A
#define MODE_PAGE_CACHING_LEN_FIELD 0x12
#define MODE_PAGE_CONTROL_LEN_FIELD 0x0A
#define MODE_SENSE_PC_CURRENT_VALUES 0
/* Log Sense defines */
#define LOG_PAGE_SUPPORTED_LOG_PAGES_PAGE 0x00
#define LOG_PAGE_SUPPORTED_LOG_PAGES_LENGTH 0x07
#define LOG_PAGE_INFORMATIONAL_EXCEPTIONS_PAGE 0x2F
#define LOG_PAGE_TEMPERATURE_PAGE 0x0D
#define LOG_SENSE_CDB_SP_OFFSET 1
#define LOG_SENSE_CDB_SP_NOT_ENABLED 0
#define LOG_SENSE_CDB_PC_OFFSET 2
#define LOG_SENSE_CDB_PC_MASK 0xC0
#define LOG_SENSE_CDB_PC_SHIFT 6
#define LOG_SENSE_CDB_PC_CUMULATIVE_VALUES 1
#define LOG_SENSE_CDB_PAGE_CODE_MASK 0x3F
#define LOG_SENSE_CDB_ALLOC_LENGTH_OFFSET 7
#define REMAINING_INFO_EXCP_PAGE_LENGTH 0x8
#define LOG_INFO_EXCP_PAGE_LENGTH 0xC
#define REMAINING_TEMP_PAGE_LENGTH 0xC
#define LOG_TEMP_PAGE_LENGTH 0x10
#define LOG_TEMP_UNKNOWN 0xFF
#define SUPPORTED_LOG_PAGES_PAGE_LENGTH 0x3
/* Read Capacity defines */
#define READ_CAP_10_RESP_SIZE 8
#define READ_CAP_16_RESP_SIZE 32
/* NVMe Namespace and Command Defines */
#define BYTES_TO_DWORDS 4
#define NVME_MAX_FIRMWARE_SLOT 7
/* Report LUNs defines */
#define REPORT_LUNS_FIRST_LUN_OFFSET 8
/* SCSI ADDITIONAL SENSE Codes */
#define SCSI_ASC_NO_SENSE 0x00
#define SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT 0x03
#define SCSI_ASC_LUN_NOT_READY 0x04
#define SCSI_ASC_WARNING 0x0B
#define SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED 0x10
#define SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED 0x10
#define SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED 0x10
#define SCSI_ASC_UNRECOVERED_READ_ERROR 0x11
#define SCSI_ASC_MISCOMPARE_DURING_VERIFY 0x1D
#define SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID 0x20
#define SCSI_ASC_ILLEGAL_COMMAND 0x20
#define SCSI_ASC_ILLEGAL_BLOCK 0x21
#define SCSI_ASC_INVALID_CDB 0x24
#define SCSI_ASC_INVALID_LUN 0x25
#define SCSI_ASC_INVALID_PARAMETER 0x26
#define SCSI_ASC_FORMAT_COMMAND_FAILED 0x31
#define SCSI_ASC_INTERNAL_TARGET_FAILURE 0x44
/* SCSI ADDITIONAL SENSE Code Qualifiers */
#define SCSI_ASCQ_CAUSE_NOT_REPORTABLE 0x00
#define SCSI_ASCQ_FORMAT_COMMAND_FAILED 0x01
#define SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED 0x01
#define SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED 0x02
#define SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED 0x03
#define SCSI_ASCQ_FORMAT_IN_PROGRESS 0x04
#define SCSI_ASCQ_POWER_LOSS_EXPECTED 0x08
#define SCSI_ASCQ_INVALID_LUN_ID 0x09
/**
* DEVICE_SPECIFIC_PARAMETER in mode parameter header (see sbc2r16) to
* enable DPOFUA support type 0x10 value.
*/
#define DEVICE_SPECIFIC_PARAMETER 0
#define VPD_ID_DESCRIPTOR_LENGTH sizeof(VPD_IDENTIFICATION_DESCRIPTOR)
/* MACROs to extract information from CDBs */
#define GET_OPCODE(cdb) cdb[0]
#define GET_U8_FROM_CDB(cdb, index) (cdb[index] << 0)
#define GET_U16_FROM_CDB(cdb, index) ((cdb[index] << 8) | (cdb[index + 1] << 0))
#define GET_U24_FROM_CDB(cdb, index) ((cdb[index] << 16) | \
(cdb[index + 1] << 8) | \
(cdb[index + 2] << 0))
#define GET_U32_FROM_CDB(cdb, index) ((cdb[index] << 24) | \
(cdb[index + 1] << 16) | \
(cdb[index + 2] << 8) | \
(cdb[index + 3] << 0))
#define GET_U64_FROM_CDB(cdb, index) ((((u64)cdb[index]) << 56) | \
(((u64)cdb[index + 1]) << 48) | \
(((u64)cdb[index + 2]) << 40) | \
(((u64)cdb[index + 3]) << 32) | \
(((u64)cdb[index + 4]) << 24) | \
(((u64)cdb[index + 5]) << 16) | \
(((u64)cdb[index + 6]) << 8) | \
(((u64)cdb[index + 7]) << 0))
/* Inquiry Helper Macros */
#define GET_INQ_EVPD_BIT(cdb) \
((GET_U8_FROM_CDB(cdb, INQUIRY_EVPD_BYTE_OFFSET) & \
INQUIRY_EVPD_BIT_MASK) ? 1 : 0)
#define GET_INQ_PAGE_CODE(cdb) \
(GET_U8_FROM_CDB(cdb, INQUIRY_PAGE_CODE_BYTE_OFFSET))
#define GET_INQ_ALLOC_LENGTH(cdb) \
(GET_U16_FROM_CDB(cdb, INQUIRY_CDB_ALLOCATION_LENGTH_OFFSET))
/* Report LUNs Helper Macros */
#define GET_REPORT_LUNS_ALLOC_LENGTH(cdb) \
(GET_U32_FROM_CDB(cdb, REPORT_LUNS_CDB_ALLOC_LENGTH_OFFSET))
/* Read Capacity Helper Macros */
#define GET_READ_CAP_16_ALLOC_LENGTH(cdb) \
(GET_U32_FROM_CDB(cdb, READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET))
#define IS_READ_CAP_16(cdb) \
((cdb[0] == SERVICE_ACTION_IN_16 && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0)
/* Request Sense Helper Macros */
#define GET_REQUEST_SENSE_ALLOC_LENGTH(cdb) \
(GET_U8_FROM_CDB(cdb, REQUEST_SENSE_CDB_ALLOC_LENGTH_OFFSET))
/* Mode Sense Helper Macros */
#define GET_MODE_SENSE_DBD(cdb) \
((GET_U8_FROM_CDB(cdb, MODE_SENSE_DBD_OFFSET) & MODE_SENSE_DBD_MASK) >> \
MODE_SENSE_DBD_SHIFT)
#define GET_MODE_SENSE_LLBAA(cdb) \
((GET_U8_FROM_CDB(cdb, MODE_SENSE_LLBAA_OFFSET) & \
MODE_SENSE_LLBAA_MASK) >> MODE_SENSE_LLBAA_SHIFT)
#define GET_MODE_SENSE_MPH_SIZE(cdb10) \
(cdb10 ? MODE_SENSE10_MPH_SIZE : MODE_SENSE6_MPH_SIZE)
/* Struct to gather data that needs to be extracted from a SCSI CDB.
Not conforming to any particular CDB variant, but compatible with all. */
struct nvme_trans_io_cdb {
u8 fua;
u8 prot_info;
u64 lba;
u32 xfer_len;
};
/* Internal Helper Functions */
/* Copy data to userspace memory */
static int nvme_trans_copy_to_user(struct sg_io_hdr *hdr, void *from,
unsigned long n)
{
int res = SNTI_TRANSLATION_SUCCESS;
unsigned long not_copied;
int i;
void *index = from;
size_t remaining = n;
size_t xfer_len;
if (hdr->iovec_count > 0) {
struct sg_iovec sgl;
for (i = 0; i < hdr->iovec_count; i++) {
not_copied = copy_from_user(&sgl, hdr->dxferp +
i * sizeof(struct sg_iovec),
sizeof(struct sg_iovec));
if (not_copied)
return -EFAULT;
xfer_len = min(remaining, sgl.iov_len);
not_copied = copy_to_user(sgl.iov_base, index,
xfer_len);
if (not_copied) {
res = -EFAULT;
break;
}
index += xfer_len;
remaining -= xfer_len;
if (remaining == 0)
break;
}
return res;
}
not_copied = copy_to_user(hdr->dxferp, from, n);
if (not_copied)
res = -EFAULT;
return res;
}
/* Copy data from userspace memory */
static int nvme_trans_copy_from_user(struct sg_io_hdr *hdr, void *to,
unsigned long n)
{
int res = SNTI_TRANSLATION_SUCCESS;
unsigned long not_copied;
int i;
void *index = to;
size_t remaining = n;
size_t xfer_len;
if (hdr->iovec_count > 0) {
struct sg_iovec sgl;
for (i = 0; i < hdr->iovec_count; i++) {
not_copied = copy_from_user(&sgl, hdr->dxferp +
i * sizeof(struct sg_iovec),
sizeof(struct sg_iovec));
if (not_copied)
return -EFAULT;
xfer_len = min(remaining, sgl.iov_len);
not_copied = copy_from_user(index, sgl.iov_base,
xfer_len);
if (not_copied) {
res = -EFAULT;
break;
}
index += xfer_len;
remaining -= xfer_len;
if (remaining == 0)
break;
}
return res;
}
not_copied = copy_from_user(to, hdr->dxferp, n);
if (not_copied)
res = -EFAULT;
return res;
}
/* Status/Sense Buffer Writeback */
static int nvme_trans_completion(struct sg_io_hdr *hdr, u8 status, u8 sense_key,
u8 asc, u8 ascq)
{
int res = SNTI_TRANSLATION_SUCCESS;
u8 xfer_len;
u8 resp[DESC_FMT_SENSE_DATA_SIZE];
if (scsi_status_is_good(status)) {
hdr->status = SAM_STAT_GOOD;
hdr->masked_status = GOOD;
hdr->host_status = DID_OK;
hdr->driver_status = DRIVER_OK;
hdr->sb_len_wr = 0;
} else {
hdr->status = status;
hdr->masked_status = status >> 1;
hdr->host_status = DID_OK;
hdr->driver_status = DRIVER_OK;
memset(resp, 0, DESC_FMT_SENSE_DATA_SIZE);
resp[0] = DESC_FORMAT_SENSE_DATA;
resp[1] = sense_key;
resp[2] = asc;
resp[3] = ascq;
xfer_len = min_t(u8, hdr->mx_sb_len, DESC_FMT_SENSE_DATA_SIZE);
hdr->sb_len_wr = xfer_len;
if (copy_to_user(hdr->sbp, resp, xfer_len) > 0)
res = -EFAULT;
}
return res;
}
static int nvme_trans_status_code(struct sg_io_hdr *hdr, int nvme_sc)
{
u8 status, sense_key, asc, ascq;
int res = SNTI_TRANSLATION_SUCCESS;
/* For non-nvme (Linux) errors, simply return the error code */
if (nvme_sc < 0)
return nvme_sc;
/* Mask DNR, More, and reserved fields */
nvme_sc &= 0x7FF;
switch (nvme_sc) {
/* Generic Command Status */
case NVME_SC_SUCCESS:
status = SAM_STAT_GOOD;
sense_key = NO_SENSE;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_INVALID_OPCODE:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_ILLEGAL_COMMAND;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_INVALID_FIELD:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_INVALID_CDB;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_DATA_XFER_ERROR:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_POWER_LOSS:
status = SAM_STAT_TASK_ABORTED;
sense_key = ABORTED_COMMAND;
asc = SCSI_ASC_WARNING;
ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED;
break;
case NVME_SC_INTERNAL:
status = SAM_STAT_CHECK_CONDITION;
sense_key = HARDWARE_ERROR;
asc = SCSI_ASC_INTERNAL_TARGET_FAILURE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_ABORT_REQ:
status = SAM_STAT_TASK_ABORTED;
sense_key = ABORTED_COMMAND;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_ABORT_QUEUE:
status = SAM_STAT_TASK_ABORTED;
sense_key = ABORTED_COMMAND;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_FUSED_FAIL:
status = SAM_STAT_TASK_ABORTED;
sense_key = ABORTED_COMMAND;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_FUSED_MISSING:
status = SAM_STAT_TASK_ABORTED;
sense_key = ABORTED_COMMAND;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_INVALID_NS:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID;
ascq = SCSI_ASCQ_INVALID_LUN_ID;
break;
case NVME_SC_LBA_RANGE:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_ILLEGAL_BLOCK;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_CAP_EXCEEDED:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_NS_NOT_READY:
status = SAM_STAT_CHECK_CONDITION;
sense_key = NOT_READY;
asc = SCSI_ASC_LUN_NOT_READY;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
/* Command Specific Status */
case NVME_SC_INVALID_FORMAT:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_FORMAT_COMMAND_FAILED;
ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED;
break;
case NVME_SC_BAD_ATTRIBUTES:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_INVALID_CDB;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
/* Media Errors */
case NVME_SC_WRITE_FAULT:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_READ_ERROR:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_UNRECOVERED_READ_ERROR;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_GUARD_CHECK:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED;
ascq = SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED;
break;
case NVME_SC_APPTAG_CHECK:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED;
ascq = SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED;
break;
case NVME_SC_REFTAG_CHECK:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MEDIUM_ERROR;
asc = SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED;
ascq = SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED;
break;
case NVME_SC_COMPARE_FAILED:
status = SAM_STAT_CHECK_CONDITION;
sense_key = MISCOMPARE;
asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
case NVME_SC_ACCESS_DENIED:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID;
ascq = SCSI_ASCQ_INVALID_LUN_ID;
break;
/* Unspecified/Default */
case NVME_SC_CMDID_CONFLICT:
case NVME_SC_CMD_SEQ_ERROR:
case NVME_SC_CQ_INVALID:
case NVME_SC_QID_INVALID:
case NVME_SC_QUEUE_SIZE:
case NVME_SC_ABORT_LIMIT:
case NVME_SC_ABORT_MISSING:
case NVME_SC_ASYNC_LIMIT:
case NVME_SC_FIRMWARE_SLOT:
case NVME_SC_FIRMWARE_IMAGE:
case NVME_SC_INVALID_VECTOR:
case NVME_SC_INVALID_LOG_PAGE:
default:
status = SAM_STAT_CHECK_CONDITION;
sense_key = ILLEGAL_REQUEST;
asc = SCSI_ASC_NO_SENSE;
ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
break;
}
res = nvme_trans_completion(hdr, status, sense_key, asc, ascq);
return res;
}
/* INQUIRY Helper Functions */
static int nvme_trans_standard_inquiry_page(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *inq_response,
int alloc_len)
{
struct nvme_dev *dev = ns->dev;
dma_addr_t dma_addr;
void *mem;
struct nvme_id_ns *id_ns;
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
int xfer_len;
u8 resp_data_format = 0x02;
u8 protect;
u8 cmdque = 0x01 << 1;
u8 fw_offset = sizeof(dev->firmware_rev);
mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
&dma_addr, GFP_KERNEL);
if (mem == NULL) {
res = -ENOMEM;
goto out_dma;
}
/* nvme ns identify - use DPS value for PROTECT field */
nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
res = nvme_trans_status_code(hdr, nvme_sc);
/*
* If nvme_sc was -ve, res will be -ve here.
* If nvme_sc was +ve, the status would bace been translated, and res
* can only be 0 or -ve.
* - If 0 && nvme_sc > 0, then go into next if where res gets nvme_sc
* - If -ve, return because its a Linux error.
*/
if (res)
goto out_free;
if (nvme_sc) {
res = nvme_sc;
goto out_free;
}
id_ns = mem;
(id_ns->dps) ? (protect = 0x01) : (protect = 0);
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
inq_response[2] = VERSION_SPC_4;
inq_response[3] = resp_data_format; /*normaca=0 | hisup=0 */
inq_response[4] = ADDITIONAL_STD_INQ_LENGTH;
inq_response[5] = protect; /* sccs=0 | acc=0 | tpgs=0 | pc3=0 */
inq_response[7] = cmdque; /* wbus16=0 | sync=0 | vs=0 */
strncpy(&inq_response[8], "NVMe ", 8);
strncpy(&inq_response[16], dev->model, 16);
while (dev->firmware_rev[fw_offset - 1] == ' ' && fw_offset > 4)
fw_offset--;
fw_offset -= 4;
strncpy(&inq_response[32], dev->firmware_rev + fw_offset, 4);
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
out_free:
dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
dma_addr);
out_dma:
return res;
}
static int nvme_trans_supported_vpd_pages(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *inq_response,
int alloc_len)
{
int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
inq_response[1] = INQ_SUPPORTED_VPD_PAGES_PAGE; /* Page Code */
inq_response[3] = INQ_NUM_SUPPORTED_VPD_PAGES; /* Page Length */
inq_response[4] = INQ_SUPPORTED_VPD_PAGES_PAGE;
inq_response[5] = INQ_UNIT_SERIAL_NUMBER_PAGE;
inq_response[6] = INQ_DEVICE_IDENTIFICATION_PAGE;
inq_response[7] = INQ_EXTENDED_INQUIRY_DATA_PAGE;
inq_response[8] = INQ_BDEV_CHARACTERISTICS_PAGE;
inq_response[9] = INQ_BDEV_LIMITS_PAGE;
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
return res;
}
static int nvme_trans_unit_serial_page(struct nvme_ns *ns,
struct sg_io_hdr *hdr, u8 *inq_response,
int alloc_len)
{
struct nvme_dev *dev = ns->dev;
int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
inq_response[1] = INQ_UNIT_SERIAL_NUMBER_PAGE; /* Page Code */
inq_response[3] = INQ_SERIAL_NUMBER_LENGTH; /* Page Length */
strncpy(&inq_response[4], dev->serial, INQ_SERIAL_NUMBER_LENGTH);
xfer_len = min(alloc_len, STANDARD_INQUIRY_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
return res;
}
static int nvme_trans_device_id_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *inq_response, int alloc_len)
{
struct nvme_dev *dev = ns->dev;
dma_addr_t dma_addr;
void *mem;
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
int xfer_len;
__be32 tmp_id = cpu_to_be32(ns->ns_id);
mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
&dma_addr, GFP_KERNEL);
if (mem == NULL) {
res = -ENOMEM;
goto out_dma;
}
memset(inq_response, 0, alloc_len);
inq_response[1] = INQ_DEVICE_IDENTIFICATION_PAGE; /* Page Code */
if (readl(&dev->bar->vs) >= NVME_VS(1, 1)) {
struct nvme_id_ns *id_ns = mem;
void *eui = id_ns->eui64;
int len = sizeof(id_ns->eui64);
nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
goto out_free;
if (nvme_sc) {
res = nvme_sc;
goto out_free;
}
if (readl(&dev->bar->vs) >= NVME_VS(1, 2)) {
if (bitmap_empty(eui, len * 8)) {
eui = id_ns->nguid;
len = sizeof(id_ns->nguid);
}
}
if (bitmap_empty(eui, len * 8))
goto scsi_string;
inq_response[3] = 4 + len; /* Page Length */
/* Designation Descriptor start */
inq_response[4] = 0x01; /* Proto ID=0h | Code set=1h */
inq_response[5] = 0x02; /* PIV=0b | Asso=00b | Designator Type=2h */
inq_response[6] = 0x00; /* Rsvd */
inq_response[7] = len; /* Designator Length */
memcpy(&inq_response[8], eui, len);
} else {
scsi_string:
if (alloc_len < 72) {
res = nvme_trans_completion(hdr,
SAM_STAT_CHECK_CONDITION,
ILLEGAL_REQUEST, SCSI_ASC_INVALID_CDB,
SCSI_ASCQ_CAUSE_NOT_REPORTABLE);
goto out_free;
}
inq_response[3] = 0x48; /* Page Length */
/* Designation Descriptor start */
inq_response[4] = 0x03; /* Proto ID=0h | Code set=3h */
inq_response[5] = 0x08; /* PIV=0b | Asso=00b | Designator Type=8h */
inq_response[6] = 0x00; /* Rsvd */
inq_response[7] = 0x44; /* Designator Length */
sprintf(&inq_response[8], "%04x", dev->pci_dev->vendor);
memcpy(&inq_response[12], dev->model, sizeof(dev->model));
sprintf(&inq_response[52], "%04x", tmp_id);
memcpy(&inq_response[56], dev->serial, sizeof(dev->serial));
}
xfer_len = alloc_len;
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
out_free:
dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
dma_addr);
out_dma:
return res;
}
static int nvme_trans_ext_inq_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
u8 *inq_response;
int res = SNTI_TRANSLATION_SUCCESS;
int nvme_sc;
struct nvme_dev *dev = ns->dev;
dma_addr_t dma_addr;
void *mem;
struct nvme_id_ctrl *id_ctrl;
struct nvme_id_ns *id_ns;
int xfer_len;
u8 microcode = 0x80;
u8 spt;
u8 spt_lut[8] = {0, 0, 2, 1, 4, 6, 5, 7};
u8 grd_chk, app_chk, ref_chk, protect;
u8 uask_sup = 0x20;
u8 v_sup;
u8 luiclr = 0x01;
inq_response = kmalloc(EXTENDED_INQUIRY_DATA_PAGE_LENGTH, GFP_KERNEL);
if (inq_response == NULL) {
res = -ENOMEM;
goto out_mem;
}
mem = dma_alloc_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns),
&dma_addr, GFP_KERNEL);
if (mem == NULL) {
res = -ENOMEM;
goto out_dma;
}
/* nvme ns identify */
nvme_sc = nvme_identify(dev, ns->ns_id, 0, dma_addr);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
goto out_free;
if (nvme_sc) {
res = nvme_sc;
goto out_free;
}
id_ns = mem;
spt = spt_lut[(id_ns->dpc) & 0x07] << 3;
(id_ns->dps) ? (protect = 0x01) : (protect = 0);
grd_chk = protect << 2;
app_chk = protect << 1;
ref_chk = protect;
/* nvme controller identify */
nvme_sc = nvme_identify(dev, 0, 1, dma_addr);
res = nvme_trans_status_code(hdr, nvme_sc);
if (res)
goto out_free;
if (nvme_sc) {
res = nvme_sc;
goto out_free;
}
id_ctrl = mem;
v_sup = id_ctrl->vwc;
memset(inq_response, 0, EXTENDED_INQUIRY_DATA_PAGE_LENGTH);
inq_response[1] = INQ_EXTENDED_INQUIRY_DATA_PAGE; /* Page Code */
inq_response[2] = 0x00; /* Page Length MSB */
inq_response[3] = 0x3C; /* Page Length LSB */
inq_response[4] = microcode | spt | grd_chk | app_chk | ref_chk;
inq_response[5] = uask_sup;
inq_response[6] = v_sup;
inq_response[7] = luiclr;
inq_response[8] = 0;
inq_response[9] = 0;
xfer_len = min(alloc_len, EXTENDED_INQUIRY_DATA_PAGE_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
out_free:
dma_free_coherent(&dev->pci_dev->dev, sizeof(struct nvme_id_ns), mem,
dma_addr);
out_dma:
kfree(inq_response);
out_mem:
return res;
}
static int nvme_trans_bdev_limits_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
u8 *inq_response, int alloc_len)
{
__be32 max_sectors = cpu_to_be32(
nvme_block_nr(ns, queue_max_hw_sectors(ns->queue)));
__be32 max_discard = cpu_to_be32(ns->queue->limits.max_discard_sectors);
__be32 discard_desc_count = cpu_to_be32(0x100);
memset(inq_response, 0, STANDARD_INQUIRY_LENGTH);
inq_response[1] = VPD_BLOCK_LIMITS;
inq_response[3] = 0x3c; /* Page Length */
memcpy(&inq_response[8], &max_sectors, sizeof(u32));
memcpy(&inq_response[20], &max_discard, sizeof(u32));
if (max_discard)
memcpy(&inq_response[24], &discard_desc_count, sizeof(u32));
return nvme_trans_copy_to_user(hdr, inq_response, 0x3c);
}
static int nvme_trans_bdev_char_page(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
u8 *inq_response;
int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
inq_response = kzalloc(EXTENDED_INQUIRY_DATA_PAGE_LENGTH, GFP_KERNEL);
if (inq_response == NULL) {
res = -ENOMEM;
goto out_mem;
}
inq_response[1] = INQ_BDEV_CHARACTERISTICS_PAGE; /* Page Code */
inq_response[2] = 0x00; /* Page Length MSB */
inq_response[3] = 0x3C; /* Page Length LSB */
inq_response[4] = 0x00; /* Medium Rotation Rate MSB */
inq_response[5] = 0x01; /* Medium Rotation Rate LSB */
inq_response[6] = 0x00; /* Form Factor */
xfer_len = min(alloc_len, EXTENDED_INQUIRY_DATA_PAGE_LENGTH);
res = nvme_trans_copy_to_user(hdr, inq_response, xfer_len);
kfree(inq_response);
out_mem:
return res;
}
/* LOG SENSE Helper Functions */
static int nvme_trans_log_supp_pages(struct nvme_ns *ns, struct sg_io_hdr *hdr,
int alloc_len)
{
int res = SNTI_TRANSLATION_SUCCESS;
int xfer_len;
u8 *log_response;