diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 16:30:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 16:30:43 -0400 |
commit | d54b3538b0bfb31351d02d1669d4a978d2abfc5f (patch) | |
tree | 5ce539ecba525b30bbfb1c46c55487099264947e /drivers/scsi/osd | |
parent | 5d80f8e5a9dc9c9a94d4aeaa567e219a808b8a4a (diff) | |
parent | af50bb993dfa673cf21ab812efe620d7e0c36319 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (119 commits)
[SCSI] scsi_dh_rdac: Retry for NOT_READY check condition
[SCSI] mpt2sas: make global symbols unique
[SCSI] sd: Make revalidate less chatty
[SCSI] sd: Try READ CAPACITY 16 first for SBC-2 devices
[SCSI] sd: Refactor sd_read_capacity()
[SCSI] mpt2sas v00.100.11.15
[SCSI] mpt2sas: add MPT2SAS_MINOR(221) to miscdevice.h
[SCSI] ch: Add scsi type modalias
[SCSI] 3w-9xxx: add power management support
[SCSI] bsg: add linux/types.h include to bsg.h
[SCSI] cxgb3i: fix function descriptions
[SCSI] libiscsi: fix possbile null ptr session command cleanup
[SCSI] iscsi class: remove host no argument from session creation callout
[SCSI] libiscsi: pass session failure a session struct
[SCSI] iscsi lib: remove qdepth param from iscsi host allocation
[SCSI] iscsi lib: have lib create work queue for transmitting IO
[SCSI] iscsi class: fix lock dep warning on logout
[SCSI] libiscsi: don't cap queue depth in iscsi modules
[SCSI] iscsi_tcp: replace scsi_debug/tcp_debug logging with iscsi conn logging
[SCSI] libiscsi_tcp: replace tcp_debug/scsi_debug logging with session/conn logging
...
Diffstat (limited to 'drivers/scsi/osd')
-rw-r--r-- | drivers/scsi/osd/Kbuild | 45 | ||||
-rw-r--r-- | drivers/scsi/osd/Kconfig | 53 | ||||
-rwxr-xr-x | drivers/scsi/osd/Makefile | 37 | ||||
-rw-r--r-- | drivers/scsi/osd/osd_debug.h | 30 | ||||
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 1657 | ||||
-rw-r--r-- | drivers/scsi/osd/osd_uld.c | 487 |
6 files changed, 2309 insertions, 0 deletions
diff --git a/drivers/scsi/osd/Kbuild b/drivers/scsi/osd/Kbuild new file mode 100644 index 000000000000..0e207aa67d16 --- /dev/null +++ b/drivers/scsi/osd/Kbuild | |||
@@ -0,0 +1,45 @@ | |||
1 | # | ||
2 | # Kbuild for the OSD modules | ||
3 | # | ||
4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
5 | # | ||
6 | # Authors: | ||
7 | # Boaz Harrosh <bharrosh@panasas.com> | ||
8 | # Benny Halevy <bhalevy@panasas.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License version 2 | ||
12 | # | ||
13 | |||
14 | ifneq ($(OSD_INC),) | ||
15 | # we are built out-of-tree Kconfigure everything as on | ||
16 | |||
17 | CONFIG_SCSI_OSD_INITIATOR=m | ||
18 | ccflags-y += -DCONFIG_SCSI_OSD_INITIATOR -DCONFIG_SCSI_OSD_INITIATOR_MODULE | ||
19 | |||
20 | CONFIG_SCSI_OSD_ULD=m | ||
21 | ccflags-y += -DCONFIG_SCSI_OSD_ULD -DCONFIG_SCSI_OSD_ULD_MODULE | ||
22 | |||
23 | # CONFIG_SCSI_OSD_DPRINT_SENSE = | ||
24 | # 0 - no print of errors | ||
25 | # 1 - print errors | ||
26 | # 2 - errors + warrnings | ||
27 | ccflags-y += -DCONFIG_SCSI_OSD_DPRINT_SENSE=1 | ||
28 | |||
29 | # Uncomment to turn debug on | ||
30 | # ccflags-y += -DCONFIG_SCSI_OSD_DEBUG | ||
31 | |||
32 | # if we are built out-of-tree and the hosting kernel has OSD headers | ||
33 | # then "ccflags-y +=" will not pick the out-off-tree headers. Only by doing | ||
34 | # this it will work. This might break in future kernels | ||
35 | LINUXINCLUDE := -I$(OSD_INC) $(LINUXINCLUDE) | ||
36 | |||
37 | endif | ||
38 | |||
39 | # libosd.ko - osd-initiator library | ||
40 | libosd-y := osd_initiator.o | ||
41 | obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o | ||
42 | |||
43 | # osd.ko - SCSI ULD and char-device | ||
44 | osd-y := osd_uld.o | ||
45 | obj-$(CONFIG_SCSI_OSD_ULD) += osd.o | ||
diff --git a/drivers/scsi/osd/Kconfig b/drivers/scsi/osd/Kconfig new file mode 100644 index 000000000000..861b5cebaeae --- /dev/null +++ b/drivers/scsi/osd/Kconfig | |||
@@ -0,0 +1,53 @@ | |||
1 | # | ||
2 | # Kernel configuration file for the OSD scsi protocol | ||
3 | # | ||
4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
5 | # | ||
6 | # Authors: | ||
7 | # Boaz Harrosh <bharrosh@panasas.com> | ||
8 | # Benny Halevy <bhalevy@panasas.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public version 2 License as | ||
12 | # published by the Free Software Foundation | ||
13 | # | ||
14 | # FIXME: SCSI_OSD_INITIATOR should select CONFIG (HMAC) SHA1 somehow. | ||
15 | # How is it done properly? | ||
16 | # | ||
17 | |||
18 | config SCSI_OSD_INITIATOR | ||
19 | tristate "OSD-Initiator library" | ||
20 | depends on SCSI | ||
21 | help | ||
22 | Enable the OSD-Initiator library (libosd.ko). | ||
23 | NOTE: You must also select CRYPTO_SHA1 + CRYPTO_HMAC and their | ||
24 | dependencies | ||
25 | |||
26 | config SCSI_OSD_ULD | ||
27 | tristate "OSD Upper Level driver" | ||
28 | depends on SCSI_OSD_INITIATOR | ||
29 | help | ||
30 | Build a SCSI upper layer driver that exports /dev/osdX devices | ||
31 | to user-mode for testing and controlling OSD devices. It is also | ||
32 | needed by exofs, for mounting an OSD based file system. | ||
33 | |||
34 | config SCSI_OSD_DPRINT_SENSE | ||
35 | int "(0-2) When sense is returned, DEBUG print all sense descriptors" | ||
36 | default 1 | ||
37 | depends on SCSI_OSD_INITIATOR | ||
38 | help | ||
39 | When a CHECK_CONDITION status is returned from a target, and a | ||
40 | sense-buffer is retrieved, turning this on will dump a full | ||
41 | sense-decoding message. Setting to 2 will also print recoverable | ||
42 | errors that might be regularly returned for some filesystem | ||
43 | operations. | ||
44 | |||
45 | config SCSI_OSD_DEBUG | ||
46 | bool "Compile All OSD modules with lots of DEBUG prints" | ||
47 | default n | ||
48 | depends on SCSI_OSD_INITIATOR | ||
49 | help | ||
50 | OSD Code is populated with lots of OSD_DEBUG(..) printouts to | ||
51 | dmesg. Enable this if you found a bug and you want to help us | ||
52 | track the problem (see also MAINTAINERS). Setting this will also | ||
53 | force SCSI_OSD_DPRINT_SENSE=2. | ||
diff --git a/drivers/scsi/osd/Makefile b/drivers/scsi/osd/Makefile new file mode 100755 index 000000000000..d905344f83ba --- /dev/null +++ b/drivers/scsi/osd/Makefile | |||
@@ -0,0 +1,37 @@ | |||
1 | # | ||
2 | # Makefile for the OSD modules (out of tree) | ||
3 | # | ||
4 | # Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
5 | # | ||
6 | # Authors: | ||
7 | # Boaz Harrosh <bharrosh@panasas.com> | ||
8 | # Benny Halevy <bhalevy@panasas.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License version 2 | ||
12 | # | ||
13 | # This Makefile is used to call the kernel Makefile in case of an out-of-tree | ||
14 | # build. | ||
15 | # $KSRC should point to a Kernel source tree otherwise host's default is | ||
16 | # used. (eg. /lib/modules/`uname -r`/build) | ||
17 | |||
18 | # include path for out-of-tree Headers | ||
19 | OSD_INC ?= `pwd`/../../../include | ||
20 | |||
21 | # allow users to override these | ||
22 | # e.g. to compile for a kernel that you aren't currently running | ||
23 | KSRC ?= /lib/modules/$(shell uname -r)/build | ||
24 | KBUILD_OUTPUT ?= | ||
25 | ARCH ?= | ||
26 | V ?= 0 | ||
27 | |||
28 | # this is the basic Kbuild out-of-tree invocation, with the M= option | ||
29 | KBUILD_BASE = +$(MAKE) -C $(KSRC) M=`pwd` KBUILD_OUTPUT=$(KBUILD_OUTPUT) ARCH=$(ARCH) V=$(V) | ||
30 | |||
31 | all: libosd | ||
32 | |||
33 | libosd: ; | ||
34 | $(KBUILD_BASE) OSD_INC=$(OSD_INC) modules | ||
35 | |||
36 | clean: | ||
37 | $(KBUILD_BASE) clean | ||
diff --git a/drivers/scsi/osd/osd_debug.h b/drivers/scsi/osd/osd_debug.h new file mode 100644 index 000000000000..579e491f11df --- /dev/null +++ b/drivers/scsi/osd/osd_debug.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * osd_debug.h - Some kprintf macros | ||
3 | * | ||
4 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
5 | * | ||
6 | * Authors: | ||
7 | * Boaz Harrosh <bharrosh@panasas.com> | ||
8 | * Benny Halevy <bhalevy@panasas.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 | ||
12 | * | ||
13 | */ | ||
14 | #ifndef __OSD_DEBUG_H__ | ||
15 | #define __OSD_DEBUG_H__ | ||
16 | |||
17 | #define OSD_ERR(fmt, a...) printk(KERN_ERR "osd: " fmt, ##a) | ||
18 | #define OSD_INFO(fmt, a...) printk(KERN_NOTICE "osd: " fmt, ##a) | ||
19 | |||
20 | #ifdef CONFIG_SCSI_OSD_DEBUG | ||
21 | #define OSD_DEBUG(fmt, a...) \ | ||
22 | printk(KERN_NOTICE "osd @%s:%d: " fmt, __func__, __LINE__, ##a) | ||
23 | #else | ||
24 | #define OSD_DEBUG(fmt, a...) do {} while (0) | ||
25 | #endif | ||
26 | |||
27 | /* u64 has problems with printk this will cast it to unsigned long long */ | ||
28 | #define _LLU(x) (unsigned long long)(x) | ||
29 | |||
30 | #endif /* ndef __OSD_DEBUG_H__ */ | ||
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c new file mode 100644 index 000000000000..552f58b655d1 --- /dev/null +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -0,0 +1,1657 @@ | |||
1 | /* | ||
2 | * osd_initiator - Main body of the osd initiator library. | ||
3 | * | ||
4 | * Note: The file does not contain the advanced security functionality which | ||
5 | * is only needed by the security_manager's initiators. | ||
6 | * | ||
7 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
8 | * | ||
9 | * Authors: | ||
10 | * Boaz Harrosh <bharrosh@panasas.com> | ||
11 | * Benny Halevy <bhalevy@panasas.com> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License version 2 | ||
15 | * | ||
16 | * Redistribution and use in source and binary forms, with or without | ||
17 | * modification, are permitted provided that the following conditions | ||
18 | * are met: | ||
19 | * | ||
20 | * 1. Redistributions of source code must retain the above copyright | ||
21 | * notice, this list of conditions and the following disclaimer. | ||
22 | * 2. Redistributions in binary form must reproduce the above copyright | ||
23 | * notice, this list of conditions and the following disclaimer in the | ||
24 | * documentation and/or other materials provided with the distribution. | ||
25 | * 3. Neither the name of the Panasas company nor the names of its | ||
26 | * contributors may be used to endorse or promote products derived | ||
27 | * from this software without specific prior written permission. | ||
28 | * | ||
29 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
30 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
31 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
32 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
33 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
34 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
35 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
36 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
37 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
38 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
39 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
40 | */ | ||
41 | |||
42 | #include <scsi/osd_initiator.h> | ||
43 | #include <scsi/osd_sec.h> | ||
44 | #include <scsi/osd_attributes.h> | ||
45 | #include <scsi/osd_sense.h> | ||
46 | |||
47 | #include <scsi/scsi_device.h> | ||
48 | |||
49 | #include "osd_debug.h" | ||
50 | |||
51 | #ifndef __unused | ||
52 | # define __unused __attribute__((unused)) | ||
53 | #endif | ||
54 | |||
55 | enum { OSD_REQ_RETRIES = 1 }; | ||
56 | |||
57 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | ||
58 | MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); | ||
59 | MODULE_LICENSE("GPL"); | ||
60 | |||
61 | static inline void build_test(void) | ||
62 | { | ||
63 | /* structures were not packed */ | ||
64 | BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN); | ||
65 | BUILD_BUG_ON(sizeof(struct osdv2_cdb) != OSD_TOTAL_CDB_LEN); | ||
66 | BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN); | ||
67 | } | ||
68 | |||
69 | static const char *_osd_ver_desc(struct osd_request *or) | ||
70 | { | ||
71 | return osd_req_is_ver1(or) ? "OSD1" : "OSD2"; | ||
72 | } | ||
73 | |||
74 | #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len) | ||
75 | |||
76 | static int _osd_print_system_info(struct osd_dev *od, void *caps) | ||
77 | { | ||
78 | struct osd_request *or; | ||
79 | struct osd_attr get_attrs[] = { | ||
80 | ATTR_DEF_RI(OSD_ATTR_RI_VENDOR_IDENTIFICATION, 8), | ||
81 | ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_IDENTIFICATION, 16), | ||
82 | ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_MODEL, 32), | ||
83 | ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_REVISION_LEVEL, 4), | ||
84 | ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER, 64 /*variable*/), | ||
85 | ATTR_DEF_RI(OSD_ATTR_RI_OSD_NAME, 64 /*variable*/), | ||
86 | ATTR_DEF_RI(OSD_ATTR_RI_TOTAL_CAPACITY, 8), | ||
87 | ATTR_DEF_RI(OSD_ATTR_RI_USED_CAPACITY, 8), | ||
88 | ATTR_DEF_RI(OSD_ATTR_RI_NUMBER_OF_PARTITIONS, 8), | ||
89 | ATTR_DEF_RI(OSD_ATTR_RI_CLOCK, 6), | ||
90 | /* IBM-OSD-SIM Has a bug with this one put it last */ | ||
91 | ATTR_DEF_RI(OSD_ATTR_RI_OSD_SYSTEM_ID, 20), | ||
92 | }; | ||
93 | void *iter = NULL, *pFirst; | ||
94 | int nelem = ARRAY_SIZE(get_attrs), a = 0; | ||
95 | int ret; | ||
96 | |||
97 | or = osd_start_request(od, GFP_KERNEL); | ||
98 | if (!or) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | /* get attrs */ | ||
102 | osd_req_get_attributes(or, &osd_root_object); | ||
103 | osd_req_add_get_attr_list(or, get_attrs, ARRAY_SIZE(get_attrs)); | ||
104 | |||
105 | ret = osd_finalize_request(or, 0, caps, NULL); | ||
106 | if (ret) | ||
107 | goto out; | ||
108 | |||
109 | ret = osd_execute_request(or); | ||
110 | if (ret) { | ||
111 | OSD_ERR("Failed to detect %s => %d\n", _osd_ver_desc(or), ret); | ||
112 | goto out; | ||
113 | } | ||
114 | |||
115 | osd_req_decode_get_attr_list(or, get_attrs, &nelem, &iter); | ||
116 | |||
117 | OSD_INFO("Detected %s device\n", | ||
118 | _osd_ver_desc(or)); | ||
119 | |||
120 | pFirst = get_attrs[a++].val_ptr; | ||
121 | OSD_INFO("OSD_ATTR_RI_VENDOR_IDENTIFICATION [%s]\n", | ||
122 | (char *)pFirst); | ||
123 | |||
124 | pFirst = get_attrs[a++].val_ptr; | ||
125 | OSD_INFO("OSD_ATTR_RI_PRODUCT_IDENTIFICATION [%s]\n", | ||
126 | (char *)pFirst); | ||
127 | |||
128 | pFirst = get_attrs[a++].val_ptr; | ||
129 | OSD_INFO("OSD_ATTR_RI_PRODUCT_MODEL [%s]\n", | ||
130 | (char *)pFirst); | ||
131 | |||
132 | pFirst = get_attrs[a++].val_ptr; | ||
133 | OSD_INFO("OSD_ATTR_RI_PRODUCT_REVISION_LEVEL [%u]\n", | ||
134 | pFirst ? get_unaligned_be32(pFirst) : ~0U); | ||
135 | |||
136 | pFirst = get_attrs[a++].val_ptr; | ||
137 | OSD_INFO("OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER [%s]\n", | ||
138 | (char *)pFirst); | ||
139 | |||
140 | pFirst = get_attrs[a].val_ptr; | ||
141 | OSD_INFO("OSD_ATTR_RI_OSD_NAME [%s]\n", (char *)pFirst); | ||
142 | a++; | ||
143 | |||
144 | pFirst = get_attrs[a++].val_ptr; | ||
145 | OSD_INFO("OSD_ATTR_RI_TOTAL_CAPACITY [0x%llx]\n", | ||
146 | pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); | ||
147 | |||
148 | pFirst = get_attrs[a++].val_ptr; | ||
149 | OSD_INFO("OSD_ATTR_RI_USED_CAPACITY [0x%llx]\n", | ||
150 | pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); | ||
151 | |||
152 | pFirst = get_attrs[a++].val_ptr; | ||
153 | OSD_INFO("OSD_ATTR_RI_NUMBER_OF_PARTITIONS [%llu]\n", | ||
154 | pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL); | ||
155 | |||
156 | if (a >= nelem) | ||
157 | goto out; | ||
158 | |||
159 | /* FIXME: Where are the time utilities */ | ||
160 | pFirst = get_attrs[a++].val_ptr; | ||
161 | OSD_INFO("OSD_ATTR_RI_CLOCK [0x%02x%02x%02x%02x%02x%02x]\n", | ||
162 | ((char *)pFirst)[0], ((char *)pFirst)[1], | ||
163 | ((char *)pFirst)[2], ((char *)pFirst)[3], | ||
164 | ((char *)pFirst)[4], ((char *)pFirst)[5]); | ||
165 | |||
166 | if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */ | ||
167 | unsigned len = get_attrs[a].len; | ||
168 | char sid_dump[32*4 + 2]; /* 2nibbles+space+ASCII */ | ||
169 | |||
170 | hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1, | ||
171 | sid_dump, sizeof(sid_dump), true); | ||
172 | OSD_INFO("OSD_ATTR_RI_OSD_SYSTEM_ID(%d) [%s]\n", len, sid_dump); | ||
173 | a++; | ||
174 | } | ||
175 | out: | ||
176 | osd_end_request(or); | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | int osd_auto_detect_ver(struct osd_dev *od, void *caps) | ||
181 | { | ||
182 | int ret; | ||
183 | |||
184 | /* Auto-detect the osd version */ | ||
185 | ret = _osd_print_system_info(od, caps); | ||
186 | if (ret) { | ||
187 | osd_dev_set_ver(od, OSD_VER1); | ||
188 | OSD_DEBUG("converting to OSD1\n"); | ||
189 | ret = _osd_print_system_info(od, caps); | ||
190 | } | ||
191 | |||
192 | return ret; | ||
193 | } | ||
194 | EXPORT_SYMBOL(osd_auto_detect_ver); | ||
195 | |||
196 | static unsigned _osd_req_cdb_len(struct osd_request *or) | ||
197 | { | ||
198 | return osd_req_is_ver1(or) ? OSDv1_TOTAL_CDB_LEN : OSD_TOTAL_CDB_LEN; | ||
199 | } | ||
200 | |||
201 | static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len) | ||
202 | { | ||
203 | return osd_req_is_ver1(or) ? | ||
204 | osdv1_attr_list_elem_size(len) : | ||
205 | osdv2_attr_list_elem_size(len); | ||
206 | } | ||
207 | |||
208 | static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head) | ||
209 | { | ||
210 | return osd_req_is_ver1(or) ? | ||
211 | osdv1_list_size(list_head) : | ||
212 | osdv2_list_size(list_head); | ||
213 | } | ||
214 | |||
215 | static unsigned _osd_req_sizeof_alist_header(struct osd_request *or) | ||
216 | { | ||
217 | return osd_req_is_ver1(or) ? | ||
218 | sizeof(struct osdv1_attributes_list_header) : | ||
219 | sizeof(struct osdv2_attributes_list_header); | ||
220 | } | ||
221 | |||
222 | static void _osd_req_set_alist_type(struct osd_request *or, | ||
223 | void *list, int list_type) | ||
224 | { | ||
225 | if (osd_req_is_ver1(or)) { | ||
226 | struct osdv1_attributes_list_header *attr_list = list; | ||
227 | |||
228 | memset(attr_list, 0, sizeof(*attr_list)); | ||
229 | attr_list->type = list_type; | ||
230 | } else { | ||
231 | struct osdv2_attributes_list_header *attr_list = list; | ||
232 | |||
233 | memset(attr_list, 0, sizeof(*attr_list)); | ||
234 | attr_list->type = list_type; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static bool _osd_req_is_alist_type(struct osd_request *or, | ||
239 | void *list, int list_type) | ||
240 | { | ||
241 | if (!list) | ||
242 | return false; | ||
243 | |||
244 | if (osd_req_is_ver1(or)) { | ||
245 | struct osdv1_attributes_list_header *attr_list = list; | ||
246 | |||
247 | return attr_list->type == list_type; | ||
248 | } else { | ||
249 | struct osdv2_attributes_list_header *attr_list = list; | ||
250 | |||
251 | return attr_list->type == list_type; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | /* This is for List-objects not Attributes-Lists */ | ||
256 | static void _osd_req_encode_olist(struct osd_request *or, | ||
257 | struct osd_obj_id_list *list) | ||
258 | { | ||
259 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
260 | |||
261 | if (osd_req_is_ver1(or)) { | ||
262 | cdbh->v1.list_identifier = list->list_identifier; | ||
263 | cdbh->v1.start_address = list->continuation_id; | ||
264 | } else { | ||
265 | cdbh->v2.list_identifier = list->list_identifier; | ||
266 | cdbh->v2.start_address = list->continuation_id; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static osd_cdb_offset osd_req_encode_offset(struct osd_request *or, | ||
271 | u64 offset, unsigned *padding) | ||
272 | { | ||
273 | return __osd_encode_offset(offset, padding, | ||
274 | osd_req_is_ver1(or) ? | ||
275 | OSDv1_OFFSET_MIN_SHIFT : OSD_OFFSET_MIN_SHIFT, | ||
276 | OSD_OFFSET_MAX_SHIFT); | ||
277 | } | ||
278 | |||
279 | static struct osd_security_parameters * | ||
280 | _osd_req_sec_params(struct osd_request *or) | ||
281 | { | ||
282 | struct osd_cdb *ocdb = &or->cdb; | ||
283 | |||
284 | if (osd_req_is_ver1(or)) | ||
285 | return &ocdb->v1.sec_params; | ||
286 | else | ||
287 | return &ocdb->v2.sec_params; | ||
288 | } | ||
289 | |||
290 | void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device) | ||
291 | { | ||
292 | memset(osdd, 0, sizeof(*osdd)); | ||
293 | osdd->scsi_device = scsi_device; | ||
294 | osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT; | ||
295 | #ifdef OSD_VER1_SUPPORT | ||
296 | osdd->version = OSD_VER2; | ||
297 | #endif | ||
298 | /* TODO: Allocate pools for osd_request attributes ... */ | ||
299 | } | ||
300 | EXPORT_SYMBOL(osd_dev_init); | ||
301 | |||
302 | void osd_dev_fini(struct osd_dev *osdd) | ||
303 | { | ||
304 | /* TODO: De-allocate pools */ | ||
305 | |||
306 | osdd->scsi_device = NULL; | ||
307 | } | ||
308 | EXPORT_SYMBOL(osd_dev_fini); | ||
309 | |||
310 | static struct osd_request *_osd_request_alloc(gfp_t gfp) | ||
311 | { | ||
312 | struct osd_request *or; | ||
313 | |||
314 | /* TODO: Use mempool with one saved request */ | ||
315 | or = kzalloc(sizeof(*or), gfp); | ||
316 | return or; | ||
317 | } | ||
318 | |||
319 | static void _osd_request_free(struct osd_request *or) | ||
320 | { | ||
321 | kfree(or); | ||
322 | } | ||
323 | |||
324 | struct osd_request *osd_start_request(struct osd_dev *dev, gfp_t gfp) | ||
325 | { | ||
326 | struct osd_request *or; | ||
327 | |||
328 | or = _osd_request_alloc(gfp); | ||
329 | if (!or) | ||
330 | return NULL; | ||
331 | |||
332 | or->osd_dev = dev; | ||
333 | or->alloc_flags = gfp; | ||
334 | or->timeout = dev->def_timeout; | ||
335 | or->retries = OSD_REQ_RETRIES; | ||
336 | |||
337 | return or; | ||
338 | } | ||
339 | EXPORT_SYMBOL(osd_start_request); | ||
340 | |||
341 | /* | ||
342 | * If osd_finalize_request() was called but the request was not executed through | ||
343 | * the block layer, then we must release BIOs. | ||
344 | */ | ||
345 | static void _abort_unexecuted_bios(struct request *rq) | ||
346 | { | ||
347 | struct bio *bio; | ||
348 | |||
349 | while ((bio = rq->bio) != NULL) { | ||
350 | rq->bio = bio->bi_next; | ||
351 | bio_endio(bio, 0); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | static void _osd_free_seg(struct osd_request *or __unused, | ||
356 | struct _osd_req_data_segment *seg) | ||
357 | { | ||
358 | if (!seg->buff || !seg->alloc_size) | ||
359 | return; | ||
360 | |||
361 | kfree(seg->buff); | ||
362 | seg->buff = NULL; | ||
363 | seg->alloc_size = 0; | ||
364 | } | ||
365 | |||
366 | void osd_end_request(struct osd_request *or) | ||
367 | { | ||
368 | struct request *rq = or->request; | ||
369 | |||
370 | _osd_free_seg(or, &or->set_attr); | ||
371 | _osd_free_seg(or, &or->enc_get_attr); | ||
372 | _osd_free_seg(or, &or->get_attr); | ||
373 | |||
374 | if (rq) { | ||
375 | if (rq->next_rq) { | ||
376 | _abort_unexecuted_bios(rq->next_rq); | ||
377 | blk_put_request(rq->next_rq); | ||
378 | } | ||
379 | |||
380 | _abort_unexecuted_bios(rq); | ||
381 | blk_put_request(rq); | ||
382 | } | ||
383 | _osd_request_free(or); | ||
384 | } | ||
385 | EXPORT_SYMBOL(osd_end_request); | ||
386 | |||
387 | int osd_execute_request(struct osd_request *or) | ||
388 | { | ||
389 | return blk_execute_rq(or->request->q, NULL, or->request, 0); | ||
390 | } | ||
391 | EXPORT_SYMBOL(osd_execute_request); | ||
392 | |||
393 | static void osd_request_async_done(struct request *req, int error) | ||
394 | { | ||
395 | struct osd_request *or = req->end_io_data; | ||
396 | |||
397 | or->async_error = error; | ||
398 | |||
399 | if (error) | ||
400 | OSD_DEBUG("osd_request_async_done error recieved %d\n", error); | ||
401 | |||
402 | if (or->async_done) | ||
403 | or->async_done(or, or->async_private); | ||
404 | else | ||
405 | osd_end_request(or); | ||
406 | } | ||
407 | |||
408 | int osd_execute_request_async(struct osd_request *or, | ||
409 | osd_req_done_fn *done, void *private) | ||
410 | { | ||
411 | or->request->end_io_data = or; | ||
412 | or->async_private = private; | ||
413 | or->async_done = done; | ||
414 | |||
415 | blk_execute_rq_nowait(or->request->q, NULL, or->request, 0, | ||
416 | osd_request_async_done); | ||
417 | return 0; | ||
418 | } | ||
419 | EXPORT_SYMBOL(osd_execute_request_async); | ||
420 | |||
421 | u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; | ||
422 | u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; | ||
423 | |||
424 | static int _osd_realloc_seg(struct osd_request *or, | ||
425 | struct _osd_req_data_segment *seg, unsigned max_bytes) | ||
426 | { | ||
427 | void *buff; | ||
428 | |||
429 | if (seg->alloc_size >= max_bytes) | ||
430 | return 0; | ||
431 | |||
432 | buff = krealloc(seg->buff, max_bytes, or->alloc_flags); | ||
433 | if (!buff) { | ||
434 | OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes, | ||
435 | seg->alloc_size); | ||
436 | return -ENOMEM; | ||
437 | } | ||
438 | |||
439 | memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size); | ||
440 | seg->buff = buff; | ||
441 | seg->alloc_size = max_bytes; | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static int _alloc_set_attr_list(struct osd_request *or, | ||
446 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) | ||
447 | { | ||
448 | unsigned total_bytes = add_bytes; | ||
449 | |||
450 | for (; nelem; --nelem, ++oa) | ||
451 | total_bytes += _osd_req_alist_elem_size(or, oa->len); | ||
452 | |||
453 | OSD_DEBUG("total_bytes=%d\n", total_bytes); | ||
454 | return _osd_realloc_seg(or, &or->set_attr, total_bytes); | ||
455 | } | ||
456 | |||
457 | static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes) | ||
458 | { | ||
459 | OSD_DEBUG("total_bytes=%d\n", max_bytes); | ||
460 | return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes); | ||
461 | } | ||
462 | |||
463 | static int _alloc_get_attr_list(struct osd_request *or) | ||
464 | { | ||
465 | OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes); | ||
466 | return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes); | ||
467 | } | ||
468 | |||
469 | /* | ||
470 | * Common to all OSD commands | ||
471 | */ | ||
472 | |||
473 | static void _osdv1_req_encode_common(struct osd_request *or, | ||
474 | __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) | ||
475 | { | ||
476 | struct osdv1_cdb *ocdb = &or->cdb.v1; | ||
477 | |||
478 | /* | ||
479 | * For speed, the commands | ||
480 | * OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C | ||
481 | * OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D | ||
482 | * are not supported here. Should pass zero and set after the call | ||
483 | */ | ||
484 | act &= cpu_to_be16(~0x0080); /* V1 action code */ | ||
485 | |||
486 | OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act)); | ||
487 | |||
488 | ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD; | ||
489 | ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH; | ||
490 | ocdb->h.varlen_cdb.service_action = act; | ||
491 | |||
492 | ocdb->h.partition = cpu_to_be64(obj->partition); | ||
493 | ocdb->h.object = cpu_to_be64(obj->id); | ||
494 | ocdb->h.v1.length = cpu_to_be64(len); | ||
495 | ocdb->h.v1.start_address = cpu_to_be64(offset); | ||
496 | } | ||
497 | |||
498 | static void _osdv2_req_encode_common(struct osd_request *or, | ||
499 | __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) | ||
500 | { | ||
501 | struct osdv2_cdb *ocdb = &or->cdb.v2; | ||
502 | |||
503 | OSD_DEBUG("OSDv2 execute opcode 0x%x\n", be16_to_cpu(act)); | ||
504 | |||
505 | ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD; | ||
506 | ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH; | ||
507 | ocdb->h.varlen_cdb.service_action = act; | ||
508 | |||
509 | ocdb->h.partition = cpu_to_be64(obj->partition); | ||
510 | ocdb->h.object = cpu_to_be64(obj->id); | ||
511 | ocdb->h.v2.length = cpu_to_be64(len); | ||
512 | ocdb->h.v2.start_address = cpu_to_be64(offset); | ||
513 | } | ||
514 | |||
515 | static void _osd_req_encode_common(struct osd_request *or, | ||
516 | __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) | ||
517 | { | ||
518 | if (osd_req_is_ver1(or)) | ||
519 | _osdv1_req_encode_common(or, act, obj, offset, len); | ||
520 | else | ||
521 | _osdv2_req_encode_common(or, act, obj, offset, len); | ||
522 | } | ||
523 | |||
524 | /* | ||
525 | * Device commands | ||
526 | */ | ||
527 | /*TODO: void osd_req_set_master_seed_xchg(struct osd_request *, ...); */ | ||
528 | /*TODO: void osd_req_set_master_key(struct osd_request *, ...); */ | ||
529 | |||
530 | void osd_req_format(struct osd_request *or, u64 tot_capacity) | ||
531 | { | ||
532 | _osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0, | ||
533 | tot_capacity); | ||
534 | } | ||
535 | EXPORT_SYMBOL(osd_req_format); | ||
536 | |||
537 | int osd_req_list_dev_partitions(struct osd_request *or, | ||
538 | osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem) | ||
539 | { | ||
540 | return osd_req_list_partition_objects(or, 0, initial_id, list, nelem); | ||
541 | } | ||
542 | EXPORT_SYMBOL(osd_req_list_dev_partitions); | ||
543 | |||
544 | static void _osd_req_encode_flush(struct osd_request *or, | ||
545 | enum osd_options_flush_scope_values op) | ||
546 | { | ||
547 | struct osd_cdb_head *ocdb = osd_cdb_head(&or->cdb); | ||
548 | |||
549 | ocdb->command_specific_options = op; | ||
550 | } | ||
551 | |||
552 | void osd_req_flush_obsd(struct osd_request *or, | ||
553 | enum osd_options_flush_scope_values op) | ||
554 | { | ||
555 | _osd_req_encode_common(or, OSD_ACT_FLUSH_OSD, &osd_root_object, 0, 0); | ||
556 | _osd_req_encode_flush(or, op); | ||
557 | } | ||
558 | EXPORT_SYMBOL(osd_req_flush_obsd); | ||
559 | |||
560 | /*TODO: void osd_req_perform_scsi_command(struct osd_request *, | ||
561 | const u8 *cdb, ...); */ | ||
562 | /*TODO: void osd_req_task_management(struct osd_request *, ...); */ | ||
563 | |||
564 | /* | ||
565 | * Partition commands | ||
566 | */ | ||
567 | static void _osd_req_encode_partition(struct osd_request *or, | ||
568 | __be16 act, osd_id partition) | ||
569 | { | ||
570 | struct osd_obj_id par = { | ||
571 | .partition = partition, | ||
572 | .id = 0, | ||
573 | }; | ||
574 | |||
575 | _osd_req_encode_common(or, act, &par, 0, 0); | ||
576 | } | ||
577 | |||
578 | void osd_req_create_partition(struct osd_request *or, osd_id partition) | ||
579 | { | ||
580 | _osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition); | ||
581 | } | ||
582 | EXPORT_SYMBOL(osd_req_create_partition); | ||
583 | |||
584 | void osd_req_remove_partition(struct osd_request *or, osd_id partition) | ||
585 | { | ||
586 | _osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition); | ||
587 | } | ||
588 | EXPORT_SYMBOL(osd_req_remove_partition); | ||
589 | |||
590 | /*TODO: void osd_req_set_partition_key(struct osd_request *, | ||
591 | osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE], | ||
592 | u8 seed[OSD_CRYPTO_SEED_SIZE]); */ | ||
593 | |||
594 | static int _osd_req_list_objects(struct osd_request *or, | ||
595 | __be16 action, const struct osd_obj_id *obj, osd_id initial_id, | ||
596 | struct osd_obj_id_list *list, unsigned nelem) | ||
597 | { | ||
598 | struct request_queue *q = or->osd_dev->scsi_device->request_queue; | ||
599 | u64 len = nelem * sizeof(osd_id) + sizeof(*list); | ||
600 | struct bio *bio; | ||
601 | |||
602 | _osd_req_encode_common(or, action, obj, (u64)initial_id, len); | ||
603 | |||
604 | if (list->list_identifier) | ||
605 | _osd_req_encode_olist(or, list); | ||
606 | |||
607 | WARN_ON(or->in.bio); | ||
608 | bio = bio_map_kern(q, list, len, or->alloc_flags); | ||
609 | if (!bio) { | ||
610 | OSD_ERR("!!! Failed to allocate list_objects BIO\n"); | ||
611 | return -ENOMEM; | ||
612 | } | ||
613 | |||
614 | bio->bi_rw &= ~(1 << BIO_RW); | ||
615 | or->in.bio = bio; | ||
616 | or->in.total_bytes = bio->bi_size; | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | int osd_req_list_partition_collections(struct osd_request *or, | ||
621 | osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, | ||
622 | unsigned nelem) | ||
623 | { | ||
624 | struct osd_obj_id par = { | ||
625 | .partition = partition, | ||
626 | .id = 0, | ||
627 | }; | ||
628 | |||
629 | return osd_req_list_collection_objects(or, &par, initial_id, list, | ||
630 | nelem); | ||
631 | } | ||
632 | EXPORT_SYMBOL(osd_req_list_partition_collections); | ||
633 | |||
634 | int osd_req_list_partition_objects(struct osd_request *or, | ||
635 | osd_id partition, osd_id initial_id, struct osd_obj_id_list *list, | ||
636 | unsigned nelem) | ||
637 | { | ||
638 | struct osd_obj_id par = { | ||
639 | .partition = partition, | ||
640 | .id = 0, | ||
641 | }; | ||
642 | |||
643 | return _osd_req_list_objects(or, OSD_ACT_LIST, &par, initial_id, list, | ||
644 | nelem); | ||
645 | } | ||
646 | EXPORT_SYMBOL(osd_req_list_partition_objects); | ||
647 | |||
648 | void osd_req_flush_partition(struct osd_request *or, | ||
649 | osd_id partition, enum osd_options_flush_scope_values op) | ||
650 | { | ||
651 | _osd_req_encode_partition(or, OSD_ACT_FLUSH_PARTITION, partition); | ||
652 | _osd_req_encode_flush(or, op); | ||
653 | } | ||
654 | EXPORT_SYMBOL(osd_req_flush_partition); | ||
655 | |||
656 | /* | ||
657 | * Collection commands | ||
658 | */ | ||
659 | /*TODO: void osd_req_create_collection(struct osd_request *, | ||
660 | const struct osd_obj_id *); */ | ||
661 | /*TODO: void osd_req_remove_collection(struct osd_request *, | ||
662 | const struct osd_obj_id *); */ | ||
663 | |||
664 | int osd_req_list_collection_objects(struct osd_request *or, | ||
665 | const struct osd_obj_id *obj, osd_id initial_id, | ||
666 | struct osd_obj_id_list *list, unsigned nelem) | ||
667 | { | ||
668 | return _osd_req_list_objects(or, OSD_ACT_LIST_COLLECTION, obj, | ||
669 | initial_id, list, nelem); | ||
670 | } | ||
671 | EXPORT_SYMBOL(osd_req_list_collection_objects); | ||
672 | |||
673 | /*TODO: void query(struct osd_request *, ...); V2 */ | ||
674 | |||
675 | void osd_req_flush_collection(struct osd_request *or, | ||
676 | const struct osd_obj_id *obj, enum osd_options_flush_scope_values op) | ||
677 | { | ||
678 | _osd_req_encode_common(or, OSD_ACT_FLUSH_PARTITION, obj, 0, 0); | ||
679 | _osd_req_encode_flush(or, op); | ||
680 | } | ||
681 | EXPORT_SYMBOL(osd_req_flush_collection); | ||
682 | |||
683 | /*TODO: void get_member_attrs(struct osd_request *, ...); V2 */ | ||
684 | /*TODO: void set_member_attrs(struct osd_request *, ...); V2 */ | ||
685 | |||
686 | /* | ||
687 | * Object commands | ||
688 | */ | ||
689 | void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj) | ||
690 | { | ||
691 | _osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0); | ||
692 | } | ||
693 | EXPORT_SYMBOL(osd_req_create_object); | ||
694 | |||
695 | void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj) | ||
696 | { | ||
697 | _osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0); | ||
698 | } | ||
699 | EXPORT_SYMBOL(osd_req_remove_object); | ||
700 | |||
701 | |||
702 | /*TODO: void osd_req_create_multi(struct osd_request *or, | ||
703 | struct osd_obj_id *first, struct osd_obj_id_list *list, unsigned nelem); | ||
704 | */ | ||
705 | |||
706 | void osd_req_write(struct osd_request *or, | ||
707 | const struct osd_obj_id *obj, struct bio *bio, u64 offset) | ||
708 | { | ||
709 | _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, bio->bi_size); | ||
710 | WARN_ON(or->out.bio || or->out.total_bytes); | ||
711 | bio->bi_rw |= (1 << BIO_RW); | ||
712 | or->out.bio = bio; | ||
713 | or->out.total_bytes = bio->bi_size; | ||
714 | } | ||
715 | EXPORT_SYMBOL(osd_req_write); | ||
716 | |||
717 | /*TODO: void osd_req_append(struct osd_request *, | ||
718 | const struct osd_obj_id *, struct bio *data_out); */ | ||
719 | /*TODO: void osd_req_create_write(struct osd_request *, | ||
720 | const struct osd_obj_id *, struct bio *data_out, u64 offset); */ | ||
721 | /*TODO: void osd_req_clear(struct osd_request *, | ||
722 | const struct osd_obj_id *, u64 offset, u64 len); */ | ||
723 | /*TODO: void osd_req_punch(struct osd_request *, | ||
724 | const struct osd_obj_id *, u64 offset, u64 len); V2 */ | ||
725 | |||
726 | void osd_req_flush_object(struct osd_request *or, | ||
727 | const struct osd_obj_id *obj, enum osd_options_flush_scope_values op, | ||
728 | /*V2*/ u64 offset, /*V2*/ u64 len) | ||
729 | { | ||
730 | if (unlikely(osd_req_is_ver1(or) && (offset || len))) { | ||
731 | OSD_DEBUG("OSD Ver1 flush on specific range ignored\n"); | ||
732 | offset = 0; | ||
733 | len = 0; | ||
734 | } | ||
735 | |||
736 | _osd_req_encode_common(or, OSD_ACT_FLUSH, obj, offset, len); | ||
737 | _osd_req_encode_flush(or, op); | ||
738 | } | ||
739 | EXPORT_SYMBOL(osd_req_flush_object); | ||
740 | |||
741 | void osd_req_read(struct osd_request *or, | ||
742 | const struct osd_obj_id *obj, struct bio *bio, u64 offset) | ||
743 | { | ||
744 | _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, bio->bi_size); | ||
745 | WARN_ON(or->in.bio || or->in.total_bytes); | ||
746 | bio->bi_rw &= ~(1 << BIO_RW); | ||
747 | or->in.bio = bio; | ||
748 | or->in.total_bytes = bio->bi_size; | ||
749 | } | ||
750 | EXPORT_SYMBOL(osd_req_read); | ||
751 | |||
752 | void osd_req_get_attributes(struct osd_request *or, | ||
753 | const struct osd_obj_id *obj) | ||
754 | { | ||
755 | _osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0); | ||
756 | } | ||
757 | EXPORT_SYMBOL(osd_req_get_attributes); | ||
758 | |||
759 | void osd_req_set_attributes(struct osd_request *or, | ||
760 | const struct osd_obj_id *obj) | ||
761 | { | ||
762 | _osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0); | ||
763 | } | ||
764 | EXPORT_SYMBOL(osd_req_set_attributes); | ||
765 | |||
766 | /* | ||
767 | * Attributes List-mode | ||
768 | */ | ||
769 | |||
770 | int osd_req_add_set_attr_list(struct osd_request *or, | ||
771 | const struct osd_attr *oa, unsigned nelem) | ||
772 | { | ||
773 | unsigned total_bytes = or->set_attr.total_bytes; | ||
774 | void *attr_last; | ||
775 | int ret; | ||
776 | |||
777 | if (or->attributes_mode && | ||
778 | or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { | ||
779 | WARN_ON(1); | ||
780 | return -EINVAL; | ||
781 | } | ||
782 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | ||
783 | |||
784 | if (!total_bytes) { /* first-time: allocate and put list header */ | ||
785 | total_bytes = _osd_req_sizeof_alist_header(or); | ||
786 | ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); | ||
787 | if (ret) | ||
788 | return ret; | ||
789 | _osd_req_set_alist_type(or, or->set_attr.buff, | ||
790 | OSD_ATTR_LIST_SET_RETRIEVE); | ||
791 | } | ||
792 | attr_last = or->set_attr.buff + total_bytes; | ||
793 | |||
794 | for (; nelem; --nelem) { | ||
795 | struct osd_attributes_list_element *attr; | ||
796 | unsigned elem_size = _osd_req_alist_elem_size(or, oa->len); | ||
797 | |||
798 | total_bytes += elem_size; | ||
799 | if (unlikely(or->set_attr.alloc_size < total_bytes)) { | ||
800 | or->set_attr.total_bytes = total_bytes - elem_size; | ||
801 | ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); | ||
802 | if (ret) | ||
803 | return ret; | ||
804 | attr_last = | ||
805 | or->set_attr.buff + or->set_attr.total_bytes; | ||
806 | } | ||
807 | |||
808 | attr = attr_last; | ||
809 | attr->attr_page = cpu_to_be32(oa->attr_page); | ||
810 | attr->attr_id = cpu_to_be32(oa->attr_id); | ||
811 | attr->attr_bytes = cpu_to_be16(oa->len); | ||
812 | memcpy(attr->attr_val, oa->val_ptr, oa->len); | ||
813 | |||
814 | attr_last += elem_size; | ||
815 | ++oa; | ||
816 | } | ||
817 | |||
818 | or->set_attr.total_bytes = total_bytes; | ||
819 | return 0; | ||
820 | } | ||
821 | EXPORT_SYMBOL(osd_req_add_set_attr_list); | ||
822 | |||
823 | static int _append_map_kern(struct request *req, | ||
824 | void *buff, unsigned len, gfp_t flags) | ||
825 | { | ||
826 | struct bio *bio; | ||
827 | int ret; | ||
828 | |||
829 | bio = bio_map_kern(req->q, buff, len, flags); | ||
830 | if (IS_ERR(bio)) { | ||
831 | OSD_ERR("Failed bio_map_kern(%p, %d) => %ld\n", buff, len, | ||
832 | PTR_ERR(bio)); | ||
833 | return PTR_ERR(bio); | ||
834 | } | ||
835 | ret = blk_rq_append_bio(req->q, req, bio); | ||
836 | if (ret) { | ||
837 | OSD_ERR("Failed blk_rq_append_bio(%p) => %d\n", bio, ret); | ||
838 | bio_put(bio); | ||
839 | } | ||
840 | return ret; | ||
841 | } | ||
842 | |||
843 | static int _req_append_segment(struct osd_request *or, | ||
844 | unsigned padding, struct _osd_req_data_segment *seg, | ||
845 | struct _osd_req_data_segment *last_seg, struct _osd_io_info *io) | ||
846 | { | ||
847 | void *pad_buff; | ||
848 | int ret; | ||
849 | |||
850 | if (padding) { | ||
851 | /* check if we can just add it to last buffer */ | ||
852 | if (last_seg && | ||
853 | (padding <= last_seg->alloc_size - last_seg->total_bytes)) | ||
854 | pad_buff = last_seg->buff + last_seg->total_bytes; | ||
855 | else | ||
856 | pad_buff = io->pad_buff; | ||
857 | |||
858 | ret = _append_map_kern(io->req, pad_buff, padding, | ||
859 | or->alloc_flags); | ||
860 | if (ret) | ||
861 | return ret; | ||
862 | io->total_bytes += padding; | ||
863 | } | ||
864 | |||
865 | ret = _append_map_kern(io->req, seg->buff, seg->total_bytes, | ||
866 | or->alloc_flags); | ||
867 | if (ret) | ||
868 | return ret; | ||
869 | |||
870 | io->total_bytes += seg->total_bytes; | ||
871 | OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff, | ||
872 | seg->total_bytes); | ||
873 | return 0; | ||
874 | } | ||
875 | |||
876 | static int _osd_req_finalize_set_attr_list(struct osd_request *or) | ||
877 | { | ||
878 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
879 | unsigned padding; | ||
880 | int ret; | ||
881 | |||
882 | if (!or->set_attr.total_bytes) { | ||
883 | cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED; | ||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes); | ||
888 | cdbh->attrs_list.set_attr_offset = | ||
889 | osd_req_encode_offset(or, or->out.total_bytes, &padding); | ||
890 | |||
891 | ret = _req_append_segment(or, padding, &or->set_attr, | ||
892 | or->out.last_seg, &or->out); | ||
893 | if (ret) | ||
894 | return ret; | ||
895 | |||
896 | or->out.last_seg = &or->set_attr; | ||
897 | return 0; | ||
898 | } | ||
899 | |||
900 | int osd_req_add_get_attr_list(struct osd_request *or, | ||
901 | const struct osd_attr *oa, unsigned nelem) | ||
902 | { | ||
903 | unsigned total_bytes = or->enc_get_attr.total_bytes; | ||
904 | void *attr_last; | ||
905 | int ret; | ||
906 | |||
907 | if (or->attributes_mode && | ||
908 | or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { | ||
909 | WARN_ON(1); | ||
910 | return -EINVAL; | ||
911 | } | ||
912 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | ||
913 | |||
914 | /* first time calc data-in list header size */ | ||
915 | if (!or->get_attr.total_bytes) | ||
916 | or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or); | ||
917 | |||
918 | /* calc data-out info */ | ||
919 | if (!total_bytes) { /* first-time: allocate and put list header */ | ||
920 | unsigned max_bytes; | ||
921 | |||
922 | total_bytes = _osd_req_sizeof_alist_header(or); | ||
923 | max_bytes = total_bytes + | ||
924 | nelem * sizeof(struct osd_attributes_list_attrid); | ||
925 | ret = _alloc_get_attr_desc(or, max_bytes); | ||
926 | if (ret) | ||
927 | return ret; | ||
928 | |||
929 | _osd_req_set_alist_type(or, or->enc_get_attr.buff, | ||
930 | OSD_ATTR_LIST_GET); | ||
931 | } | ||
932 | attr_last = or->enc_get_attr.buff + total_bytes; | ||
933 | |||
934 | for (; nelem; --nelem) { | ||
935 | struct osd_attributes_list_attrid *attrid; | ||
936 | const unsigned cur_size = sizeof(*attrid); | ||
937 | |||
938 | total_bytes += cur_size; | ||
939 | if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) { | ||
940 | or->enc_get_attr.total_bytes = total_bytes - cur_size; | ||
941 | ret = _alloc_get_attr_desc(or, | ||
942 | total_bytes + nelem * sizeof(*attrid)); | ||
943 | if (ret) | ||
944 | return ret; | ||
945 | attr_last = or->enc_get_attr.buff + | ||
946 | or->enc_get_attr.total_bytes; | ||
947 | } | ||
948 | |||
949 | attrid = attr_last; | ||
950 | attrid->attr_page = cpu_to_be32(oa->attr_page); | ||
951 | attrid->attr_id = cpu_to_be32(oa->attr_id); | ||
952 | |||
953 | attr_last += cur_size; | ||
954 | |||
955 | /* calc data-in size */ | ||
956 | or->get_attr.total_bytes += | ||
957 | _osd_req_alist_elem_size(or, oa->len); | ||
958 | ++oa; | ||
959 | } | ||
960 | |||
961 | or->enc_get_attr.total_bytes = total_bytes; | ||
962 | |||
963 | OSD_DEBUG( | ||
964 | "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%Zu)\n", | ||
965 | or->get_attr.total_bytes, | ||
966 | or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or), | ||
967 | or->enc_get_attr.total_bytes, | ||
968 | (or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or)) | ||
969 | / sizeof(struct osd_attributes_list_attrid)); | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | EXPORT_SYMBOL(osd_req_add_get_attr_list); | ||
974 | |||
975 | static int _osd_req_finalize_get_attr_list(struct osd_request *or) | ||
976 | { | ||
977 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
978 | unsigned out_padding; | ||
979 | unsigned in_padding; | ||
980 | int ret; | ||
981 | |||
982 | if (!or->enc_get_attr.total_bytes) { | ||
983 | cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED; | ||
984 | cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED; | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | ret = _alloc_get_attr_list(or); | ||
989 | if (ret) | ||
990 | return ret; | ||
991 | |||
992 | /* The out-going buffer info update */ | ||
993 | OSD_DEBUG("out-going\n"); | ||
994 | cdbh->attrs_list.get_attr_desc_bytes = | ||
995 | cpu_to_be32(or->enc_get_attr.total_bytes); | ||
996 | |||
997 | cdbh->attrs_list.get_attr_desc_offset = | ||
998 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); | ||
999 | |||
1000 | ret = _req_append_segment(or, out_padding, &or->enc_get_attr, | ||
1001 | or->out.last_seg, &or->out); | ||
1002 | if (ret) | ||
1003 | return ret; | ||
1004 | or->out.last_seg = &or->enc_get_attr; | ||
1005 | |||
1006 | /* The incoming buffer info update */ | ||
1007 | OSD_DEBUG("in-coming\n"); | ||
1008 | cdbh->attrs_list.get_attr_alloc_length = | ||
1009 | cpu_to_be32(or->get_attr.total_bytes); | ||
1010 | |||
1011 | cdbh->attrs_list.get_attr_offset = | ||
1012 | osd_req_encode_offset(or, or->in.total_bytes, &in_padding); | ||
1013 | |||
1014 | ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, | ||
1015 | &or->in); | ||
1016 | if (ret) | ||
1017 | return ret; | ||
1018 | or->in.last_seg = &or->get_attr; | ||
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | int osd_req_decode_get_attr_list(struct osd_request *or, | ||
1024 | struct osd_attr *oa, int *nelem, void **iterator) | ||
1025 | { | ||
1026 | unsigned cur_bytes, returned_bytes; | ||
1027 | int n; | ||
1028 | const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or); | ||
1029 | void *cur_p; | ||
1030 | |||
1031 | if (!_osd_req_is_alist_type(or, or->get_attr.buff, | ||
1032 | OSD_ATTR_LIST_SET_RETRIEVE)) { | ||
1033 | oa->attr_page = 0; | ||
1034 | oa->attr_id = 0; | ||
1035 | oa->val_ptr = NULL; | ||
1036 | oa->len = 0; | ||
1037 | *iterator = NULL; | ||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | if (*iterator) { | ||
1042 | BUG_ON((*iterator < or->get_attr.buff) || | ||
1043 | (or->get_attr.buff + or->get_attr.alloc_size < *iterator)); | ||
1044 | cur_p = *iterator; | ||
1045 | cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list; | ||
1046 | returned_bytes = or->get_attr.total_bytes; | ||
1047 | } else { /* first time decode the list header */ | ||
1048 | cur_bytes = sizeof_attr_list; | ||
1049 | returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) + | ||
1050 | sizeof_attr_list; | ||
1051 | |||
1052 | cur_p = or->get_attr.buff + sizeof_attr_list; | ||
1053 | |||
1054 | if (returned_bytes > or->get_attr.alloc_size) { | ||
1055 | OSD_DEBUG("target report: space was not big enough! " | ||
1056 | "Allocate=%u Needed=%u\n", | ||
1057 | or->get_attr.alloc_size, | ||
1058 | returned_bytes + sizeof_attr_list); | ||
1059 | |||
1060 | returned_bytes = | ||
1061 | or->get_attr.alloc_size - sizeof_attr_list; | ||
1062 | } | ||
1063 | or->get_attr.total_bytes = returned_bytes; | ||
1064 | } | ||
1065 | |||
1066 | for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) { | ||
1067 | struct osd_attributes_list_element *attr = cur_p; | ||
1068 | unsigned inc; | ||
1069 | |||
1070 | oa->len = be16_to_cpu(attr->attr_bytes); | ||
1071 | inc = _osd_req_alist_elem_size(or, oa->len); | ||
1072 | OSD_DEBUG("oa->len=%d inc=%d cur_bytes=%d\n", | ||
1073 | oa->len, inc, cur_bytes); | ||
1074 | cur_bytes += inc; | ||
1075 | if (cur_bytes > returned_bytes) { | ||
1076 | OSD_ERR("BAD FOOD from target. list not valid!" | ||
1077 | "c=%d r=%d n=%d\n", | ||
1078 | cur_bytes, returned_bytes, n); | ||
1079 | oa->val_ptr = NULL; | ||
1080 | break; | ||
1081 | } | ||
1082 | |||
1083 | oa->attr_page = be32_to_cpu(attr->attr_page); | ||
1084 | oa->attr_id = be32_to_cpu(attr->attr_id); | ||
1085 | oa->val_ptr = attr->attr_val; | ||
1086 | |||
1087 | cur_p += inc; | ||
1088 | ++oa; | ||
1089 | } | ||
1090 | |||
1091 | *iterator = (returned_bytes - cur_bytes) ? cur_p : NULL; | ||
1092 | *nelem = n; | ||
1093 | return returned_bytes - cur_bytes; | ||
1094 | } | ||
1095 | EXPORT_SYMBOL(osd_req_decode_get_attr_list); | ||
1096 | |||
1097 | /* | ||
1098 | * Attributes Page-mode | ||
1099 | */ | ||
1100 | |||
1101 | int osd_req_add_get_attr_page(struct osd_request *or, | ||
1102 | u32 page_id, void *attar_page, unsigned max_page_len, | ||
1103 | const struct osd_attr *set_one_attr) | ||
1104 | { | ||
1105 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
1106 | |||
1107 | if (or->attributes_mode && | ||
1108 | or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) { | ||
1109 | WARN_ON(1); | ||
1110 | return -EINVAL; | ||
1111 | } | ||
1112 | or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE; | ||
1113 | |||
1114 | or->get_attr.buff = attar_page; | ||
1115 | or->get_attr.total_bytes = max_page_len; | ||
1116 | |||
1117 | or->set_attr.buff = set_one_attr->val_ptr; | ||
1118 | or->set_attr.total_bytes = set_one_attr->len; | ||
1119 | |||
1120 | cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id); | ||
1121 | cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len); | ||
1122 | /* ocdb->attrs_page.get_attr_offset; */ | ||
1123 | |||
1124 | cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page); | ||
1125 | cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id); | ||
1126 | cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len); | ||
1127 | /* ocdb->attrs_page.set_attr_offset; */ | ||
1128 | return 0; | ||
1129 | } | ||
1130 | EXPORT_SYMBOL(osd_req_add_get_attr_page); | ||
1131 | |||
1132 | static int _osd_req_finalize_attr_page(struct osd_request *or) | ||
1133 | { | ||
1134 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
1135 | unsigned in_padding, out_padding; | ||
1136 | int ret; | ||
1137 | |||
1138 | /* returned page */ | ||
1139 | cdbh->attrs_page.get_attr_offset = | ||
1140 | osd_req_encode_offset(or, or->in.total_bytes, &in_padding); | ||
1141 | |||
1142 | ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, | ||
1143 | &or->in); | ||
1144 | if (ret) | ||
1145 | return ret; | ||
1146 | |||
1147 | /* set one value */ | ||
1148 | cdbh->attrs_page.set_attr_offset = | ||
1149 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); | ||
1150 | |||
1151 | ret = _req_append_segment(or, out_padding, &or->enc_get_attr, NULL, | ||
1152 | &or->out); | ||
1153 | return ret; | ||
1154 | } | ||
1155 | |||
1156 | static int _osd_req_finalize_data_integrity(struct osd_request *or, | ||
1157 | bool has_in, bool has_out, const u8 *cap_key) | ||
1158 | { | ||
1159 | struct osd_security_parameters *sec_parms = _osd_req_sec_params(or); | ||
1160 | int ret; | ||
1161 | |||
1162 | if (!osd_is_sec_alldata(sec_parms)) | ||
1163 | return 0; | ||
1164 | |||
1165 | if (has_out) { | ||
1166 | struct _osd_req_data_segment seg = { | ||
1167 | .buff = &or->out_data_integ, | ||
1168 | .total_bytes = sizeof(or->out_data_integ), | ||
1169 | }; | ||
1170 | unsigned pad; | ||
1171 | |||
1172 | or->out_data_integ.data_bytes = cpu_to_be64( | ||
1173 | or->out.bio ? or->out.bio->bi_size : 0); | ||
1174 | or->out_data_integ.set_attributes_bytes = cpu_to_be64( | ||
1175 | or->set_attr.total_bytes); | ||
1176 | or->out_data_integ.get_attributes_bytes = cpu_to_be64( | ||
1177 | or->enc_get_attr.total_bytes); | ||
1178 | |||
1179 | sec_parms->data_out_integrity_check_offset = | ||
1180 | osd_req_encode_offset(or, or->out.total_bytes, &pad); | ||
1181 | |||
1182 | ret = _req_append_segment(or, pad, &seg, or->out.last_seg, | ||
1183 | &or->out); | ||
1184 | if (ret) | ||
1185 | return ret; | ||
1186 | or->out.last_seg = NULL; | ||
1187 | |||
1188 | /* they are now all chained to request sign them all together */ | ||
1189 | osd_sec_sign_data(&or->out_data_integ, or->out.req->bio, | ||
1190 | cap_key); | ||
1191 | } | ||
1192 | |||
1193 | if (has_in) { | ||
1194 | struct _osd_req_data_segment seg = { | ||
1195 | .buff = &or->in_data_integ, | ||
1196 | .total_bytes = sizeof(or->in_data_integ), | ||
1197 | }; | ||
1198 | unsigned pad; | ||
1199 | |||
1200 | sec_parms->data_in_integrity_check_offset = | ||
1201 | osd_req_encode_offset(or, or->in.total_bytes, &pad); | ||
1202 | |||
1203 | ret = _req_append_segment(or, pad, &seg, or->in.last_seg, | ||
1204 | &or->in); | ||
1205 | if (ret) | ||
1206 | return ret; | ||
1207 | |||
1208 | or->in.last_seg = NULL; | ||
1209 | } | ||
1210 | |||
1211 | return 0; | ||
1212 | } | ||
1213 | |||
1214 | /* | ||
1215 | * osd_finalize_request and helpers | ||
1216 | */ | ||
1217 | |||
1218 | static int _init_blk_request(struct osd_request *or, | ||
1219 | bool has_in, bool has_out) | ||
1220 | { | ||
1221 | gfp_t flags = or->alloc_flags; | ||
1222 | struct scsi_device *scsi_device = or->osd_dev->scsi_device; | ||
1223 | struct request_queue *q = scsi_device->request_queue; | ||
1224 | struct request *req; | ||
1225 | int ret = -ENOMEM; | ||
1226 | |||
1227 | req = blk_get_request(q, has_out, flags); | ||
1228 | if (!req) | ||
1229 | goto out; | ||
1230 | |||
1231 | or->request = req; | ||
1232 | req->cmd_type = REQ_TYPE_BLOCK_PC; | ||
1233 | req->timeout = or->timeout; | ||
1234 | req->retries = or->retries; | ||
1235 | req->sense = or->sense; | ||
1236 | req->sense_len = 0; | ||
1237 | |||
1238 | if (has_out) { | ||
1239 | or->out.req = req; | ||
1240 | if (has_in) { | ||
1241 | /* allocate bidi request */ | ||
1242 | req = blk_get_request(q, READ, flags); | ||
1243 | if (!req) { | ||
1244 | OSD_DEBUG("blk_get_request for bidi failed\n"); | ||
1245 | goto out; | ||
1246 | } | ||
1247 | req->cmd_type = REQ_TYPE_BLOCK_PC; | ||
1248 | or->in.req = or->request->next_rq = req; | ||
1249 | } | ||
1250 | } else if (has_in) | ||
1251 | or->in.req = req; | ||
1252 | |||
1253 | ret = 0; | ||
1254 | out: | ||
1255 | OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n", | ||
1256 | or, has_in, has_out, ret, or->request); | ||
1257 | return ret; | ||
1258 | } | ||
1259 | |||
1260 | int osd_finalize_request(struct osd_request *or, | ||
1261 | u8 options, const void *cap, const u8 *cap_key) | ||
1262 | { | ||
1263 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
1264 | bool has_in, has_out; | ||
1265 | int ret; | ||
1266 | |||
1267 | if (options & OSD_REQ_FUA) | ||
1268 | cdbh->options |= OSD_CDB_FUA; | ||
1269 | |||
1270 | if (options & OSD_REQ_DPO) | ||
1271 | cdbh->options |= OSD_CDB_DPO; | ||
1272 | |||
1273 | if (options & OSD_REQ_BYPASS_TIMESTAMPS) | ||
1274 | cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS; | ||
1275 | |||
1276 | osd_set_caps(&or->cdb, cap); | ||
1277 | |||
1278 | has_in = or->in.bio || or->get_attr.total_bytes; | ||
1279 | has_out = or->out.bio || or->set_attr.total_bytes || | ||
1280 | or->enc_get_attr.total_bytes; | ||
1281 | |||
1282 | ret = _init_blk_request(or, has_in, has_out); | ||
1283 | if (ret) { | ||
1284 | OSD_DEBUG("_init_blk_request failed\n"); | ||
1285 | return ret; | ||
1286 | } | ||
1287 | |||
1288 | if (or->out.bio) { | ||
1289 | ret = blk_rq_append_bio(or->request->q, or->out.req, | ||
1290 | or->out.bio); | ||
1291 | if (ret) { | ||
1292 | OSD_DEBUG("blk_rq_append_bio out failed\n"); | ||
1293 | return ret; | ||
1294 | } | ||
1295 | OSD_DEBUG("out bytes=%llu (bytes_req=%u)\n", | ||
1296 | _LLU(or->out.total_bytes), or->out.req->data_len); | ||
1297 | } | ||
1298 | if (or->in.bio) { | ||
1299 | ret = blk_rq_append_bio(or->request->q, or->in.req, or->in.bio); | ||
1300 | if (ret) { | ||
1301 | OSD_DEBUG("blk_rq_append_bio in failed\n"); | ||
1302 | return ret; | ||
1303 | } | ||
1304 | OSD_DEBUG("in bytes=%llu (bytes_req=%u)\n", | ||
1305 | _LLU(or->in.total_bytes), or->in.req->data_len); | ||
1306 | } | ||
1307 | |||
1308 | or->out.pad_buff = sg_out_pad_buffer; | ||
1309 | or->in.pad_buff = sg_in_pad_buffer; | ||
1310 | |||
1311 | if (!or->attributes_mode) | ||
1312 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | ||
1313 | cdbh->command_specific_options |= or->attributes_mode; | ||
1314 | if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) { | ||
1315 | ret = _osd_req_finalize_attr_page(or); | ||
1316 | } else { | ||
1317 | /* TODO: I think that for the GET_ATTR command these 2 should | ||
1318 | * be reversed to keep them in execution order (for embeded | ||
1319 | * targets with low memory footprint) | ||
1320 | */ | ||
1321 | ret = _osd_req_finalize_set_attr_list(or); | ||
1322 | if (ret) { | ||
1323 | OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n"); | ||
1324 | return ret; | ||
1325 | } | ||
1326 | |||
1327 | ret = _osd_req_finalize_get_attr_list(or); | ||
1328 | if (ret) { | ||
1329 | OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n"); | ||
1330 | return ret; | ||
1331 | } | ||
1332 | } | ||
1333 | |||
1334 | ret = _osd_req_finalize_data_integrity(or, has_in, has_out, cap_key); | ||
1335 | if (ret) | ||
1336 | return ret; | ||
1337 | |||
1338 | osd_sec_sign_cdb(&or->cdb, cap_key); | ||
1339 | |||
1340 | or->request->cmd = or->cdb.buff; | ||
1341 | or->request->cmd_len = _osd_req_cdb_len(or); | ||
1342 | |||
1343 | return 0; | ||
1344 | } | ||
1345 | EXPORT_SYMBOL(osd_finalize_request); | ||
1346 | |||
1347 | #define OSD_SENSE_PRINT1(fmt, a...) \ | ||
1348 | do { \ | ||
1349 | if (__cur_sense_need_output) \ | ||
1350 | OSD_ERR(fmt, ##a); \ | ||
1351 | } while (0) | ||
1352 | |||
1353 | #define OSD_SENSE_PRINT2(fmt, a...) OSD_SENSE_PRINT1(" " fmt, ##a) | ||
1354 | |||
1355 | int osd_req_decode_sense_full(struct osd_request *or, | ||
1356 | struct osd_sense_info *osi, bool silent, | ||
1357 | struct osd_obj_id *bad_obj_list __unused, int max_obj __unused, | ||
1358 | struct osd_attr *bad_attr_list, int max_attr) | ||
1359 | { | ||
1360 | int sense_len, original_sense_len; | ||
1361 | struct osd_sense_info local_osi; | ||
1362 | struct scsi_sense_descriptor_based *ssdb; | ||
1363 | void *cur_descriptor; | ||
1364 | #if (CONFIG_SCSI_OSD_DPRINT_SENSE == 0) | ||
1365 | const bool __cur_sense_need_output = false; | ||
1366 | #else | ||
1367 | bool __cur_sense_need_output = !silent; | ||
1368 | #endif | ||
1369 | |||
1370 | if (!or->request->errors) | ||
1371 | return 0; | ||
1372 | |||
1373 | ssdb = or->request->sense; | ||
1374 | sense_len = or->request->sense_len; | ||
1375 | if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) { | ||
1376 | OSD_ERR("Block-layer returned error(0x%x) but " | ||
1377 | "sense_len(%u) || key(%d) is empty\n", | ||
1378 | or->request->errors, sense_len, ssdb->sense_key); | ||
1379 | return -EIO; | ||
1380 | } | ||
1381 | |||
1382 | if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) { | ||
1383 | OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n", | ||
1384 | ssdb->response_code, sense_len); | ||
1385 | return -EIO; | ||
1386 | } | ||
1387 | |||
1388 | osi = osi ? : &local_osi; | ||
1389 | memset(osi, 0, sizeof(*osi)); | ||
1390 | osi->key = ssdb->sense_key; | ||
1391 | osi->additional_code = be16_to_cpu(ssdb->additional_sense_code); | ||
1392 | original_sense_len = ssdb->additional_sense_length + 8; | ||
1393 | |||
1394 | #if (CONFIG_SCSI_OSD_DPRINT_SENSE == 1) | ||
1395 | if (__cur_sense_need_output) | ||
1396 | __cur_sense_need_output = (osi->key > scsi_sk_recovered_error); | ||
1397 | #endif | ||
1398 | OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) " | ||
1399 | "additional_code=0x%x\n", | ||
1400 | osi->key, original_sense_len, sense_len, | ||
1401 | osi->additional_code); | ||
1402 | |||
1403 | if (original_sense_len < sense_len) | ||
1404 | sense_len = original_sense_len; | ||
1405 | |||
1406 | cur_descriptor = ssdb->ssd; | ||
1407 | sense_len -= sizeof(*ssdb); | ||
1408 | while (sense_len > 0) { | ||
1409 | struct scsi_sense_descriptor *ssd = cur_descriptor; | ||
1410 | int cur_len = ssd->additional_length + 2; | ||
1411 | |||
1412 | sense_len -= cur_len; | ||
1413 | |||
1414 | if (sense_len < 0) | ||
1415 | break; /* sense was truncated */ | ||
1416 | |||
1417 | switch (ssd->descriptor_type) { | ||
1418 | case scsi_sense_information: | ||
1419 | case scsi_sense_command_specific_information: | ||
1420 | { | ||
1421 | struct scsi_sense_command_specific_data_descriptor | ||
1422 | *sscd = cur_descriptor; | ||
1423 | |||
1424 | osi->command_info = | ||
1425 | get_unaligned_be64(&sscd->information) ; | ||
1426 | OSD_SENSE_PRINT2( | ||
1427 | "command_specific_information 0x%llx \n", | ||
1428 | _LLU(osi->command_info)); | ||
1429 | break; | ||
1430 | } | ||
1431 | case scsi_sense_key_specific: | ||
1432 | { | ||
1433 | struct scsi_sense_key_specific_data_descriptor | ||
1434 | *ssks = cur_descriptor; | ||
1435 | |||
1436 | osi->sense_info = get_unaligned_be16(&ssks->value); | ||
1437 | OSD_SENSE_PRINT2( | ||
1438 | "sense_key_specific_information %u" | ||
1439 | "sksv_cd_bpv_bp (0x%x)\n", | ||
1440 | osi->sense_info, ssks->sksv_cd_bpv_bp); | ||
1441 | break; | ||
1442 | } | ||
1443 | case osd_sense_object_identification: | ||
1444 | { /*FIXME: Keep first not last, Store in array*/ | ||
1445 | struct osd_sense_identification_data_descriptor | ||
1446 | *osidd = cur_descriptor; | ||
1447 | |||
1448 | osi->not_initiated_command_functions = | ||
1449 | le32_to_cpu(osidd->not_initiated_functions); | ||
1450 | osi->completed_command_functions = | ||
1451 | le32_to_cpu(osidd->completed_functions); | ||
1452 | osi->obj.partition = be64_to_cpu(osidd->partition_id); | ||
1453 | osi->obj.id = be64_to_cpu(osidd->object_id); | ||
1454 | OSD_SENSE_PRINT2( | ||
1455 | "object_identification pid=0x%llx oid=0x%llx\n", | ||
1456 | _LLU(osi->obj.partition), _LLU(osi->obj.id)); | ||
1457 | OSD_SENSE_PRINT2( | ||
1458 | "not_initiated_bits(%x) " | ||
1459 | "completed_command_bits(%x)\n", | ||
1460 | osi->not_initiated_command_functions, | ||
1461 | osi->completed_command_functions); | ||
1462 | break; | ||
1463 | } | ||
1464 | case osd_sense_response_integrity_check: | ||
1465 | { | ||
1466 | struct osd_sense_response_integrity_check_descriptor | ||
1467 | *osricd = cur_descriptor; | ||
1468 | const unsigned len = | ||
1469 | sizeof(osricd->integrity_check_value); | ||
1470 | char key_dump[len*4 + 2]; /* 2nibbles+space+ASCII */ | ||
1471 | |||
1472 | hex_dump_to_buffer(osricd->integrity_check_value, len, | ||
1473 | 32, 1, key_dump, sizeof(key_dump), true); | ||
1474 | OSD_SENSE_PRINT2("response_integrity [%s]\n", key_dump); | ||
1475 | } | ||
1476 | case osd_sense_attribute_identification: | ||
1477 | { | ||
1478 | struct osd_sense_attributes_data_descriptor | ||
1479 | *osadd = cur_descriptor; | ||
1480 | int len = min(cur_len, sense_len); | ||
1481 | int i = 0; | ||
1482 | struct osd_sense_attr *pattr = osadd->sense_attrs; | ||
1483 | |||
1484 | while (len < 0) { | ||
1485 | u32 attr_page = be32_to_cpu(pattr->attr_page); | ||
1486 | u32 attr_id = be32_to_cpu(pattr->attr_id); | ||
1487 | |||
1488 | if (i++ == 0) { | ||
1489 | osi->attr.attr_page = attr_page; | ||
1490 | osi->attr.attr_id = attr_id; | ||
1491 | } | ||
1492 | |||
1493 | if (bad_attr_list && max_attr) { | ||
1494 | bad_attr_list->attr_page = attr_page; | ||
1495 | bad_attr_list->attr_id = attr_id; | ||
1496 | bad_attr_list++; | ||
1497 | max_attr--; | ||
1498 | } | ||
1499 | OSD_SENSE_PRINT2( | ||
1500 | "osd_sense_attribute_identification" | ||
1501 | "attr_page=0x%x attr_id=0x%x\n", | ||
1502 | attr_page, attr_id); | ||
1503 | } | ||
1504 | } | ||
1505 | /*These are not legal for OSD*/ | ||
1506 | case scsi_sense_field_replaceable_unit: | ||
1507 | OSD_SENSE_PRINT2("scsi_sense_field_replaceable_unit\n"); | ||
1508 | break; | ||
1509 | case scsi_sense_stream_commands: | ||
1510 | OSD_SENSE_PRINT2("scsi_sense_stream_commands\n"); | ||
1511 | break; | ||
1512 | case scsi_sense_block_commands: | ||
1513 | OSD_SENSE_PRINT2("scsi_sense_block_commands\n"); | ||
1514 | break; | ||
1515 | case scsi_sense_ata_return: | ||
1516 | OSD_SENSE_PRINT2("scsi_sense_ata_return\n"); | ||
1517 | break; | ||
1518 | default: | ||
1519 | if (ssd->descriptor_type <= scsi_sense_Reserved_last) | ||
1520 | OSD_SENSE_PRINT2( | ||
1521 | "scsi_sense Reserved descriptor (0x%x)", | ||
1522 | ssd->descriptor_type); | ||
1523 | else | ||
1524 | OSD_SENSE_PRINT2( | ||
1525 | "scsi_sense Vendor descriptor (0x%x)", | ||
1526 | ssd->descriptor_type); | ||
1527 | } | ||
1528 | |||
1529 | cur_descriptor += cur_len; | ||
1530 | } | ||
1531 | |||
1532 | return (osi->key > scsi_sk_recovered_error) ? -EIO : 0; | ||
1533 | } | ||
1534 | EXPORT_SYMBOL(osd_req_decode_sense_full); | ||
1535 | |||
1536 | /* | ||
1537 | * Implementation of osd_sec.h API | ||
1538 | * TODO: Move to a separate osd_sec.c file at a later stage. | ||
1539 | */ | ||
1540 | |||
1541 | enum { OSD_SEC_CAP_V1_ALL_CAPS = | ||
1542 | OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE | | ||
1543 | OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR | | ||
1544 | OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC | | ||
1545 | OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT | ||
1546 | }; | ||
1547 | |||
1548 | enum { OSD_SEC_CAP_V2_ALL_CAPS = | ||
1549 | OSD_SEC_CAP_V1_ALL_CAPS | OSD_SEC_CAP_QUERY | OSD_SEC_CAP_M_OBJECT | ||
1550 | }; | ||
1551 | |||
1552 | void osd_sec_init_nosec_doall_caps(void *caps, | ||
1553 | const struct osd_obj_id *obj, bool is_collection, const bool is_v1) | ||
1554 | { | ||
1555 | struct osd_capability *cap = caps; | ||
1556 | u8 type; | ||
1557 | u8 descriptor_type; | ||
1558 | |||
1559 | if (likely(obj->id)) { | ||
1560 | if (unlikely(is_collection)) { | ||
1561 | type = OSD_SEC_OBJ_COLLECTION; | ||
1562 | descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ : | ||
1563 | OSD_SEC_OBJ_DESC_COL; | ||
1564 | } else { | ||
1565 | type = OSD_SEC_OBJ_USER; | ||
1566 | descriptor_type = OSD_SEC_OBJ_DESC_OBJ; | ||
1567 | } | ||
1568 | WARN_ON(!obj->partition); | ||
1569 | } else { | ||
1570 | type = obj->partition ? OSD_SEC_OBJ_PARTITION : | ||
1571 | OSD_SEC_OBJ_ROOT; | ||
1572 | descriptor_type = OSD_SEC_OBJ_DESC_PAR; | ||
1573 | } | ||
1574 | |||
1575 | memset(cap, 0, sizeof(*cap)); | ||
1576 | |||
1577 | cap->h.format = OSD_SEC_CAP_FORMAT_VER1; | ||
1578 | cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */ | ||
1579 | cap->h.security_method = OSD_SEC_NOSEC; | ||
1580 | /* cap->expiration_time; | ||
1581 | cap->AUDIT[30-10]; | ||
1582 | cap->discriminator[42-30]; | ||
1583 | cap->object_created_time; */ | ||
1584 | cap->h.object_type = type; | ||
1585 | osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS); | ||
1586 | cap->h.object_descriptor_type = descriptor_type; | ||
1587 | cap->od.obj_desc.policy_access_tag = 0; | ||
1588 | cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition); | ||
1589 | cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id); | ||
1590 | } | ||
1591 | EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps); | ||
1592 | |||
1593 | /* FIXME: Extract version from caps pointer. | ||
1594 | * Also Pete's target only supports caps from OSDv1 for now | ||
1595 | */ | ||
1596 | void osd_set_caps(struct osd_cdb *cdb, const void *caps) | ||
1597 | { | ||
1598 | bool is_ver1 = true; | ||
1599 | /* NOTE: They start at same address */ | ||
1600 | memcpy(&cdb->v1.caps, caps, is_ver1 ? OSDv1_CAP_LEN : OSD_CAP_LEN); | ||
1601 | } | ||
1602 | |||
1603 | bool osd_is_sec_alldata(struct osd_security_parameters *sec_parms __unused) | ||
1604 | { | ||
1605 | return false; | ||
1606 | } | ||
1607 | |||
1608 | void osd_sec_sign_cdb(struct osd_cdb *ocdb __unused, const u8 *cap_key __unused) | ||
1609 | { | ||
1610 | } | ||
1611 | |||
1612 | void osd_sec_sign_data(void *data_integ __unused, | ||
1613 | struct bio *bio __unused, const u8 *cap_key __unused) | ||
1614 | { | ||
1615 | } | ||
1616 | |||
1617 | /* | ||
1618 | * Declared in osd_protocol.h | ||
1619 | * 4.12.5 Data-In and Data-Out buffer offsets | ||
1620 | * byte offset = mantissa * (2^(exponent+8)) | ||
1621 | * Returns the smallest allowed encoded offset that contains given @offset | ||
1622 | * The actual encoded offset returned is @offset + *@padding. | ||
1623 | */ | ||
1624 | osd_cdb_offset __osd_encode_offset( | ||
1625 | u64 offset, unsigned *padding, int min_shift, int max_shift) | ||
1626 | { | ||
1627 | u64 try_offset = -1, mod, align; | ||
1628 | osd_cdb_offset be32_offset; | ||
1629 | int shift; | ||
1630 | |||
1631 | *padding = 0; | ||
1632 | if (!offset) | ||
1633 | return 0; | ||
1634 | |||
1635 | for (shift = min_shift; shift < max_shift; ++shift) { | ||
1636 | try_offset = offset >> shift; | ||
1637 | if (try_offset < (1 << OSD_OFFSET_MAX_BITS)) | ||
1638 | break; | ||
1639 | } | ||
1640 | |||
1641 | BUG_ON(shift == max_shift); | ||
1642 | |||
1643 | align = 1 << shift; | ||
1644 | mod = offset & (align - 1); | ||
1645 | if (mod) { | ||
1646 | *padding = align - mod; | ||
1647 | try_offset += 1; | ||
1648 | } | ||
1649 | |||
1650 | try_offset |= ((shift - 8) & 0xf) << 28; | ||
1651 | be32_offset = cpu_to_be32((u32)try_offset); | ||
1652 | |||
1653 | OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n", | ||
1654 | _LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift, | ||
1655 | be32_offset, *padding); | ||
1656 | return be32_offset; | ||
1657 | } | ||
diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c new file mode 100644 index 000000000000..f8b1a749958b --- /dev/null +++ b/drivers/scsi/osd/osd_uld.c | |||
@@ -0,0 +1,487 @@ | |||
1 | /* | ||
2 | * osd_uld.c - OSD Upper Layer Driver | ||
3 | * | ||
4 | * A Linux driver module that registers as a SCSI ULD and probes | ||
5 | * for OSD type SCSI devices. | ||
6 | * It's main function is to export osd devices to in-kernel users like | ||
7 | * osdfs and pNFS-objects-LD. It also provides one ioctl for running | ||
8 | * in Kernel tests. | ||
9 | * | ||
10 | * Copyright (C) 2008 Panasas Inc. All rights reserved. | ||
11 | * | ||
12 | * Authors: | ||
13 | * Boaz Harrosh <bharrosh@panasas.com> | ||
14 | * Benny Halevy <bhalevy@panasas.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License version 2 | ||
18 | * | ||
19 | * Redistribution and use in source and binary forms, with or without | ||
20 | * modification, are permitted provided that the following conditions | ||
21 | * are met: | ||
22 | * | ||
23 | * 1. Redistributions of source code must retain the above copyright | ||
24 | * notice, this list of conditions and the following disclaimer. | ||
25 | * 2. Redistributions in binary form must reproduce the above copyright | ||
26 | * notice, this list of conditions and the following disclaimer in the | ||
27 | * documentation and/or other materials provided with the distribution. | ||
28 | * 3. Neither the name of the Panasas company nor the names of its | ||
29 | * contributors may be used to endorse or promote products derived | ||
30 | * from this software without specific prior written permission. | ||
31 | * | ||
32 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
33 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
34 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
35 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
36 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
37 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
38 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
39 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
40 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
41 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
42 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
43 | */ | ||
44 | |||
45 | #include <linux/namei.h> | ||
46 | #include <linux/cdev.h> | ||
47 | #include <linux/fs.h> | ||
48 | #include <linux/module.h> | ||
49 | #include <linux/device.h> | ||
50 | #include <linux/idr.h> | ||
51 | #include <linux/major.h> | ||
52 | |||
53 | #include <scsi/scsi.h> | ||
54 | #include <scsi/scsi_driver.h> | ||
55 | #include <scsi/scsi_device.h> | ||
56 | #include <scsi/scsi_ioctl.h> | ||
57 | |||
58 | #include <scsi/osd_initiator.h> | ||
59 | #include <scsi/osd_sec.h> | ||
60 | |||
61 | #include "osd_debug.h" | ||
62 | |||
63 | #ifndef TYPE_OSD | ||
64 | # define TYPE_OSD 0x11 | ||
65 | #endif | ||
66 | |||
67 | #ifndef SCSI_OSD_MAJOR | ||
68 | # define SCSI_OSD_MAJOR 260 | ||
69 | #endif | ||
70 | #define SCSI_OSD_MAX_MINOR 64 | ||
71 | |||
72 | static const char osd_name[] = "osd"; | ||
73 | static const char *osd_version_string = "open-osd 0.1.0"; | ||
74 | const char osd_symlink[] = "scsi_osd"; | ||
75 | |||
76 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | ||
77 | MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko"); | ||
78 | MODULE_LICENSE("GPL"); | ||
79 | MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR); | ||
80 | MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD); | ||
81 | |||
82 | struct osd_uld_device { | ||
83 | int minor; | ||
84 | struct kref kref; | ||
85 | struct cdev cdev; | ||
86 | struct osd_dev od; | ||
87 | struct gendisk *disk; | ||
88 | struct device *class_member; | ||
89 | }; | ||
90 | |||
91 | static void __uld_get(struct osd_uld_device *oud); | ||
92 | static void __uld_put(struct osd_uld_device *oud); | ||
93 | |||
94 | /* | ||
95 | * Char Device operations | ||
96 | */ | ||
97 | |||
98 | static int osd_uld_open(struct inode *inode, struct file *file) | ||
99 | { | ||
100 | struct osd_uld_device *oud = container_of(inode->i_cdev, | ||
101 | struct osd_uld_device, cdev); | ||
102 | |||
103 | __uld_get(oud); | ||
104 | /* cache osd_uld_device on file handle */ | ||
105 | file->private_data = oud; | ||
106 | OSD_DEBUG("osd_uld_open %p\n", oud); | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static int osd_uld_release(struct inode *inode, struct file *file) | ||
111 | { | ||
112 | struct osd_uld_device *oud = file->private_data; | ||
113 | |||
114 | OSD_DEBUG("osd_uld_release %p\n", file->private_data); | ||
115 | file->private_data = NULL; | ||
116 | __uld_put(oud); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /* FIXME: Only one vector for now */ | ||
121 | unsigned g_test_ioctl; | ||
122 | do_test_fn *g_do_test; | ||
123 | |||
124 | int osduld_register_test(unsigned ioctl, do_test_fn *do_test) | ||
125 | { | ||
126 | if (g_test_ioctl) | ||
127 | return -EINVAL; | ||
128 | |||
129 | g_test_ioctl = ioctl; | ||
130 | g_do_test = do_test; | ||
131 | return 0; | ||
132 | } | ||
133 | EXPORT_SYMBOL(osduld_register_test); | ||
134 | |||
135 | void osduld_unregister_test(unsigned ioctl) | ||
136 | { | ||
137 | if (ioctl == g_test_ioctl) { | ||
138 | g_test_ioctl = 0; | ||
139 | g_do_test = NULL; | ||
140 | } | ||
141 | } | ||
142 | EXPORT_SYMBOL(osduld_unregister_test); | ||
143 | |||
144 | static do_test_fn *_find_ioctl(unsigned cmd) | ||
145 | { | ||
146 | if (g_test_ioctl == cmd) | ||
147 | return g_do_test; | ||
148 | else | ||
149 | return NULL; | ||
150 | } | ||
151 | |||
152 | static long osd_uld_ioctl(struct file *file, unsigned int cmd, | ||
153 | unsigned long arg) | ||
154 | { | ||
155 | struct osd_uld_device *oud = file->private_data; | ||
156 | int ret; | ||
157 | do_test_fn *do_test; | ||
158 | |||
159 | do_test = _find_ioctl(cmd); | ||
160 | if (do_test) | ||
161 | ret = do_test(&oud->od, cmd, arg); | ||
162 | else { | ||
163 | OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud); | ||
164 | ret = -ENOIOCTLCMD; | ||
165 | } | ||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static const struct file_operations osd_fops = { | ||
170 | .owner = THIS_MODULE, | ||
171 | .open = osd_uld_open, | ||
172 | .release = osd_uld_release, | ||
173 | .unlocked_ioctl = osd_uld_ioctl, | ||
174 | }; | ||
175 | |||
176 | struct osd_dev *osduld_path_lookup(const char *path) | ||
177 | { | ||
178 | struct nameidata nd; | ||
179 | struct inode *inode; | ||
180 | struct cdev *cdev; | ||
181 | struct osd_uld_device *uninitialized_var(oud); | ||
182 | int error; | ||
183 | |||
184 | if (!path || !*path) { | ||
185 | OSD_ERR("Mount with !path || !*path\n"); | ||
186 | return ERR_PTR(-EINVAL); | ||
187 | } | ||
188 | |||
189 | error = path_lookup(path, LOOKUP_FOLLOW, &nd); | ||
190 | if (error) { | ||
191 | OSD_ERR("path_lookup of %s faild=>%d\n", path, error); | ||
192 | return ERR_PTR(error); | ||
193 | } | ||
194 | |||
195 | inode = nd.path.dentry->d_inode; | ||
196 | error = -EINVAL; /* Not the right device e.g osd_uld_device */ | ||
197 | if (!S_ISCHR(inode->i_mode)) { | ||
198 | OSD_DEBUG("!S_ISCHR()\n"); | ||
199 | goto out; | ||
200 | } | ||
201 | |||
202 | cdev = inode->i_cdev; | ||
203 | if (!cdev) { | ||
204 | OSD_ERR("Before mounting an OSD Based filesystem\n"); | ||
205 | OSD_ERR(" user-mode must open+close the %s device\n", path); | ||
206 | OSD_ERR(" Example: bash: echo < %s\n", path); | ||
207 | goto out; | ||
208 | } | ||
209 | |||
210 | /* The Magic wand. Is it our char-dev */ | ||
211 | /* TODO: Support sg devices */ | ||
212 | if (cdev->owner != THIS_MODULE) { | ||
213 | OSD_ERR("Error mounting %s - is not an OSD device\n", path); | ||
214 | goto out; | ||
215 | } | ||
216 | |||
217 | oud = container_of(cdev, struct osd_uld_device, cdev); | ||
218 | |||
219 | __uld_get(oud); | ||
220 | error = 0; | ||
221 | |||
222 | out: | ||
223 | path_put(&nd.path); | ||
224 | return error ? ERR_PTR(error) : &oud->od; | ||
225 | } | ||
226 | EXPORT_SYMBOL(osduld_path_lookup); | ||
227 | |||
228 | void osduld_put_device(struct osd_dev *od) | ||
229 | { | ||
230 | if (od) { | ||
231 | struct osd_uld_device *oud = container_of(od, | ||
232 | struct osd_uld_device, od); | ||
233 | |||
234 | __uld_put(oud); | ||
235 | } | ||
236 | } | ||
237 | EXPORT_SYMBOL(osduld_put_device); | ||
238 | |||
239 | /* | ||
240 | * Scsi Device operations | ||
241 | */ | ||
242 | |||
243 | static int __detect_osd(struct osd_uld_device *oud) | ||
244 | { | ||
245 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
246 | char caps[OSD_CAP_LEN]; | ||
247 | int error; | ||
248 | |||
249 | /* sending a test_unit_ready as first command seems to be needed | ||
250 | * by some targets | ||
251 | */ | ||
252 | OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n", | ||
253 | oud, scsi_device, scsi_device->request_queue); | ||
254 | error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, NULL); | ||
255 | if (error) | ||
256 | OSD_ERR("warning: scsi_test_unit_ready failed\n"); | ||
257 | |||
258 | osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true); | ||
259 | if (osd_auto_detect_ver(&oud->od, caps)) | ||
260 | return -ENODEV; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | static struct class *osd_sysfs_class; | ||
266 | static DEFINE_IDA(osd_minor_ida); | ||
267 | |||
268 | static int osd_probe(struct device *dev) | ||
269 | { | ||
270 | struct scsi_device *scsi_device = to_scsi_device(dev); | ||
271 | struct gendisk *disk; | ||
272 | struct osd_uld_device *oud; | ||
273 | int minor; | ||
274 | int error; | ||
275 | |||
276 | if (scsi_device->type != TYPE_OSD) | ||
277 | return -ENODEV; | ||
278 | |||
279 | do { | ||
280 | if (!ida_pre_get(&osd_minor_ida, GFP_KERNEL)) | ||
281 | return -ENODEV; | ||
282 | |||
283 | error = ida_get_new(&osd_minor_ida, &minor); | ||
284 | } while (error == -EAGAIN); | ||
285 | |||
286 | if (error) | ||
287 | return error; | ||
288 | if (minor >= SCSI_OSD_MAX_MINOR) { | ||
289 | error = -EBUSY; | ||
290 | goto err_retract_minor; | ||
291 | } | ||
292 | |||
293 | error = -ENOMEM; | ||
294 | oud = kzalloc(sizeof(*oud), GFP_KERNEL); | ||
295 | if (NULL == oud) | ||
296 | goto err_retract_minor; | ||
297 | |||
298 | kref_init(&oud->kref); | ||
299 | dev_set_drvdata(dev, oud); | ||
300 | oud->minor = minor; | ||
301 | |||
302 | /* allocate a disk and set it up */ | ||
303 | /* FIXME: do we need this since sg has already done that */ | ||
304 | disk = alloc_disk(1); | ||
305 | if (!disk) { | ||
306 | OSD_ERR("alloc_disk failed\n"); | ||
307 | goto err_free_osd; | ||
308 | } | ||
309 | disk->major = SCSI_OSD_MAJOR; | ||
310 | disk->first_minor = oud->minor; | ||
311 | sprintf(disk->disk_name, "osd%d", oud->minor); | ||
312 | oud->disk = disk; | ||
313 | |||
314 | /* hold one more reference to the scsi_device that will get released | ||
315 | * in __release, in case a logout is happening while fs is mounted | ||
316 | */ | ||
317 | scsi_device_get(scsi_device); | ||
318 | osd_dev_init(&oud->od, scsi_device); | ||
319 | |||
320 | /* Detect the OSD Version */ | ||
321 | error = __detect_osd(oud); | ||
322 | if (error) { | ||
323 | OSD_ERR("osd detection failed, non-compatible OSD device\n"); | ||
324 | goto err_put_disk; | ||
325 | } | ||
326 | |||
327 | /* init the char-device for communication with user-mode */ | ||
328 | cdev_init(&oud->cdev, &osd_fops); | ||
329 | oud->cdev.owner = THIS_MODULE; | ||
330 | error = cdev_add(&oud->cdev, | ||
331 | MKDEV(SCSI_OSD_MAJOR, oud->minor), 1); | ||
332 | if (error) { | ||
333 | OSD_ERR("cdev_add failed\n"); | ||
334 | goto err_put_disk; | ||
335 | } | ||
336 | kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */ | ||
337 | |||
338 | /* class_member */ | ||
339 | oud->class_member = device_create(osd_sysfs_class, dev, | ||
340 | MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name); | ||
341 | if (IS_ERR(oud->class_member)) { | ||
342 | OSD_ERR("class_device_create failed\n"); | ||
343 | error = PTR_ERR(oud->class_member); | ||
344 | goto err_put_cdev; | ||
345 | } | ||
346 | |||
347 | dev_set_drvdata(oud->class_member, oud); | ||
348 | error = sysfs_create_link(&scsi_device->sdev_gendev.kobj, | ||
349 | &oud->class_member->kobj, osd_symlink); | ||
350 | if (error) | ||
351 | OSD_ERR("warning: unable to make symlink\n"); | ||
352 | |||
353 | OSD_INFO("osd_probe %s\n", disk->disk_name); | ||
354 | return 0; | ||
355 | |||
356 | err_put_cdev: | ||
357 | cdev_del(&oud->cdev); | ||
358 | err_put_disk: | ||
359 | scsi_device_put(scsi_device); | ||
360 | put_disk(disk); | ||
361 | err_free_osd: | ||
362 | dev_set_drvdata(dev, NULL); | ||
363 | kfree(oud); | ||
364 | err_retract_minor: | ||
365 | ida_remove(&osd_minor_ida, minor); | ||
366 | return error; | ||
367 | } | ||
368 | |||
369 | static int osd_remove(struct device *dev) | ||
370 | { | ||
371 | struct scsi_device *scsi_device = to_scsi_device(dev); | ||
372 | struct osd_uld_device *oud = dev_get_drvdata(dev); | ||
373 | |||
374 | if (!oud || (oud->od.scsi_device != scsi_device)) { | ||
375 | OSD_ERR("Half cooked osd-device %p,%p || %p!=%p", | ||
376 | dev, oud, oud ? oud->od.scsi_device : NULL, | ||
377 | scsi_device); | ||
378 | } | ||
379 | |||
380 | sysfs_remove_link(&oud->od.scsi_device->sdev_gendev.kobj, osd_symlink); | ||
381 | |||
382 | if (oud->class_member) | ||
383 | device_destroy(osd_sysfs_class, | ||
384 | MKDEV(SCSI_OSD_MAJOR, oud->minor)); | ||
385 | |||
386 | /* We have 2 references to the cdev. One is released here | ||
387 | * and also takes down the /dev/osdX mapping. The second | ||
388 | * Will be released in __remove() after all users have released | ||
389 | * the osd_uld_device. | ||
390 | */ | ||
391 | if (oud->cdev.owner) | ||
392 | cdev_del(&oud->cdev); | ||
393 | |||
394 | __uld_put(oud); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static void __remove(struct kref *kref) | ||
399 | { | ||
400 | struct osd_uld_device *oud = container_of(kref, | ||
401 | struct osd_uld_device, kref); | ||
402 | struct scsi_device *scsi_device = oud->od.scsi_device; | ||
403 | |||
404 | /* now let delete the char_dev */ | ||
405 | kobject_put(&oud->cdev.kobj); | ||
406 | |||
407 | osd_dev_fini(&oud->od); | ||
408 | scsi_device_put(scsi_device); | ||
409 | |||
410 | OSD_INFO("osd_remove %s\n", | ||
411 | oud->disk ? oud->disk->disk_name : NULL); | ||
412 | |||
413 | if (oud->disk) | ||
414 | put_disk(oud->disk); | ||
415 | |||
416 | ida_remove(&osd_minor_ida, oud->minor); | ||
417 | kfree(oud); | ||
418 | } | ||
419 | |||
420 | static void __uld_get(struct osd_uld_device *oud) | ||
421 | { | ||
422 | kref_get(&oud->kref); | ||
423 | } | ||
424 | |||
425 | static void __uld_put(struct osd_uld_device *oud) | ||
426 | { | ||
427 | kref_put(&oud->kref, __remove); | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * Global driver and scsi registration | ||
432 | */ | ||
433 | |||
434 | static struct scsi_driver osd_driver = { | ||
435 | .owner = THIS_MODULE, | ||
436 | .gendrv = { | ||
437 | .name = osd_name, | ||
438 | .probe = osd_probe, | ||
439 | .remove = osd_remove, | ||
440 | } | ||
441 | }; | ||
442 | |||
443 | static int __init osd_uld_init(void) | ||
444 | { | ||
445 | int err; | ||
446 | |||
447 | osd_sysfs_class = class_create(THIS_MODULE, osd_symlink); | ||
448 | if (IS_ERR(osd_sysfs_class)) { | ||
449 | OSD_ERR("Unable to register sysfs class => %ld\n", | ||
450 | PTR_ERR(osd_sysfs_class)); | ||
451 | return PTR_ERR(osd_sysfs_class); | ||
452 | } | ||
453 | |||
454 | err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), | ||
455 | SCSI_OSD_MAX_MINOR, osd_name); | ||
456 | if (err) { | ||
457 | OSD_ERR("Unable to register major %d for osd ULD => %d\n", | ||
458 | SCSI_OSD_MAJOR, err); | ||
459 | goto err_out; | ||
460 | } | ||
461 | |||
462 | err = scsi_register_driver(&osd_driver.gendrv); | ||
463 | if (err) { | ||
464 | OSD_ERR("scsi_register_driver failed => %d\n", err); | ||
465 | goto err_out_chrdev; | ||
466 | } | ||
467 | |||
468 | OSD_INFO("LOADED %s\n", osd_version_string); | ||
469 | return 0; | ||
470 | |||
471 | err_out_chrdev: | ||
472 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | ||
473 | err_out: | ||
474 | class_destroy(osd_sysfs_class); | ||
475 | return err; | ||
476 | } | ||
477 | |||
478 | static void __exit osd_uld_exit(void) | ||
479 | { | ||
480 | scsi_unregister_driver(&osd_driver.gendrv); | ||
481 | unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR); | ||
482 | class_destroy(osd_sysfs_class); | ||
483 | OSD_INFO("UNLOADED %s\n", osd_version_string); | ||
484 | } | ||
485 | |||
486 | module_init(osd_uld_init); | ||
487 | module_exit(osd_uld_exit); | ||