1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /******************************************************************************* |
3 | * Filename: target_core_ua.c |
4 | * |
5 | * This file contains logic for SPC-3 Unit Attention emulation |
6 | * |
7 | * (c) Copyright 2009-2013 Datera, Inc. |
8 | * |
9 | * Nicholas A. Bellinger <nab@kernel.org> |
10 | * |
11 | ******************************************************************************/ |
12 | |
13 | #include <linux/slab.h> |
14 | #include <linux/spinlock.h> |
15 | #include <scsi/scsi_proto.h> |
16 | |
17 | #include <target/target_core_base.h> |
18 | #include <target/target_core_fabric.h> |
19 | |
20 | #include "target_core_internal.h" |
21 | #include "target_core_alua.h" |
22 | #include "target_core_pr.h" |
23 | #include "target_core_ua.h" |
24 | |
25 | sense_reason_t |
26 | target_scsi3_ua_check(struct se_cmd *cmd) |
27 | { |
28 | struct se_dev_entry *deve; |
29 | struct se_session *sess = cmd->se_sess; |
30 | struct se_node_acl *nacl; |
31 | |
32 | if (!sess) |
33 | return 0; |
34 | |
35 | nacl = sess->se_node_acl; |
36 | if (!nacl) |
37 | return 0; |
38 | |
39 | rcu_read_lock(); |
40 | deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun); |
41 | if (!deve) { |
42 | rcu_read_unlock(); |
43 | return 0; |
44 | } |
45 | if (list_empty_careful(head: &deve->ua_list)) { |
46 | rcu_read_unlock(); |
47 | return 0; |
48 | } |
49 | rcu_read_unlock(); |
50 | /* |
51 | * From sam4r14, section 5.14 Unit attention condition: |
52 | * |
53 | * a) if an INQUIRY command enters the enabled command state, the |
54 | * device server shall process the INQUIRY command and shall neither |
55 | * report nor clear any unit attention condition; |
56 | * b) if a REPORT LUNS command enters the enabled command state, the |
57 | * device server shall process the REPORT LUNS command and shall not |
58 | * report any unit attention condition; |
59 | * e) if a REQUEST SENSE command enters the enabled command state while |
60 | * a unit attention condition exists for the SCSI initiator port |
61 | * associated with the I_T nexus on which the REQUEST SENSE command |
62 | * was received, then the device server shall process the command |
63 | * and either: |
64 | */ |
65 | switch (cmd->t_task_cdb[0]) { |
66 | case INQUIRY: |
67 | case REPORT_LUNS: |
68 | case REQUEST_SENSE: |
69 | return 0; |
70 | default: |
71 | return TCM_CHECK_CONDITION_UNIT_ATTENTION; |
72 | } |
73 | } |
74 | |
75 | int core_scsi3_ua_allocate( |
76 | struct se_dev_entry *deve, |
77 | u8 asc, |
78 | u8 ascq) |
79 | { |
80 | struct se_ua *ua, *ua_p, *ua_tmp; |
81 | |
82 | ua = kmem_cache_zalloc(k: se_ua_cache, GFP_ATOMIC); |
83 | if (!ua) { |
84 | pr_err("Unable to allocate struct se_ua\n" ); |
85 | return -ENOMEM; |
86 | } |
87 | INIT_LIST_HEAD(list: &ua->ua_nacl_list); |
88 | |
89 | ua->ua_asc = asc; |
90 | ua->ua_ascq = ascq; |
91 | |
92 | spin_lock(lock: &deve->ua_lock); |
93 | list_for_each_entry_safe(ua_p, ua_tmp, &deve->ua_list, ua_nacl_list) { |
94 | /* |
95 | * Do not report the same UNIT ATTENTION twice.. |
96 | */ |
97 | if ((ua_p->ua_asc == asc) && (ua_p->ua_ascq == ascq)) { |
98 | spin_unlock(lock: &deve->ua_lock); |
99 | kmem_cache_free(s: se_ua_cache, objp: ua); |
100 | return 0; |
101 | } |
102 | /* |
103 | * Attach the highest priority Unit Attention to |
104 | * the head of the list following sam4r14, |
105 | * Section 5.14 Unit Attention Condition: |
106 | * |
107 | * POWER ON, RESET, OR BUS DEVICE RESET OCCURRED highest |
108 | * POWER ON OCCURRED or |
109 | * DEVICE INTERNAL RESET |
110 | * SCSI BUS RESET OCCURRED or |
111 | * MICROCODE HAS BEEN CHANGED or |
112 | * protocol specific |
113 | * BUS DEVICE RESET FUNCTION OCCURRED |
114 | * I_T NEXUS LOSS OCCURRED |
115 | * COMMANDS CLEARED BY POWER LOSS NOTIFICATION |
116 | * all others Lowest |
117 | * |
118 | * Each of the ASCQ codes listed above are defined in |
119 | * the 29h ASC family, see spc4r17 Table D.1 |
120 | */ |
121 | if (ua_p->ua_asc == 0x29) { |
122 | if ((asc == 0x29) && (ascq > ua_p->ua_ascq)) |
123 | list_add(new: &ua->ua_nacl_list, |
124 | head: &deve->ua_list); |
125 | else |
126 | list_add_tail(new: &ua->ua_nacl_list, |
127 | head: &deve->ua_list); |
128 | } else if (ua_p->ua_asc == 0x2a) { |
129 | /* |
130 | * Incoming Family 29h ASCQ codes will override |
131 | * Family 2AHh ASCQ codes for Unit Attention condition. |
132 | */ |
133 | if ((asc == 0x29) || (ascq > ua_p->ua_asc)) |
134 | list_add(new: &ua->ua_nacl_list, |
135 | head: &deve->ua_list); |
136 | else |
137 | list_add_tail(new: &ua->ua_nacl_list, |
138 | head: &deve->ua_list); |
139 | } else |
140 | list_add_tail(new: &ua->ua_nacl_list, |
141 | head: &deve->ua_list); |
142 | spin_unlock(lock: &deve->ua_lock); |
143 | |
144 | return 0; |
145 | } |
146 | list_add_tail(new: &ua->ua_nacl_list, head: &deve->ua_list); |
147 | spin_unlock(lock: &deve->ua_lock); |
148 | |
149 | pr_debug("Allocated UNIT ATTENTION, mapped LUN: %llu, ASC:" |
150 | " 0x%02x, ASCQ: 0x%02x\n" , deve->mapped_lun, |
151 | asc, ascq); |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | void target_ua_allocate_lun(struct se_node_acl *nacl, |
157 | u32 unpacked_lun, u8 asc, u8 ascq) |
158 | { |
159 | struct se_dev_entry *deve; |
160 | |
161 | if (!nacl) |
162 | return; |
163 | |
164 | rcu_read_lock(); |
165 | deve = target_nacl_find_deve(nacl, unpacked_lun); |
166 | if (!deve) { |
167 | rcu_read_unlock(); |
168 | return; |
169 | } |
170 | |
171 | core_scsi3_ua_allocate(deve, asc, ascq); |
172 | rcu_read_unlock(); |
173 | } |
174 | |
175 | void core_scsi3_ua_release_all( |
176 | struct se_dev_entry *deve) |
177 | { |
178 | struct se_ua *ua, *ua_p; |
179 | |
180 | spin_lock(lock: &deve->ua_lock); |
181 | list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) { |
182 | list_del(entry: &ua->ua_nacl_list); |
183 | kmem_cache_free(s: se_ua_cache, objp: ua); |
184 | } |
185 | spin_unlock(lock: &deve->ua_lock); |
186 | } |
187 | |
188 | /* |
189 | * Dequeue a unit attention from the unit attention list. This function |
190 | * returns true if the dequeuing succeeded and if *@key, *@asc and *@ascq have |
191 | * been set. |
192 | */ |
193 | bool core_scsi3_ua_for_check_condition(struct se_cmd *cmd, u8 *key, u8 *asc, |
194 | u8 *ascq) |
195 | { |
196 | struct se_device *dev = cmd->se_dev; |
197 | struct se_dev_entry *deve; |
198 | struct se_session *sess = cmd->se_sess; |
199 | struct se_node_acl *nacl; |
200 | struct se_ua *ua = NULL, *ua_p; |
201 | int head = 1; |
202 | bool dev_ua_intlck_clear = (dev->dev_attrib.emulate_ua_intlck_ctrl |
203 | == TARGET_UA_INTLCK_CTRL_CLEAR); |
204 | |
205 | if (WARN_ON_ONCE(!sess)) |
206 | return false; |
207 | |
208 | nacl = sess->se_node_acl; |
209 | if (WARN_ON_ONCE(!nacl)) |
210 | return false; |
211 | |
212 | rcu_read_lock(); |
213 | deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun); |
214 | if (!deve) { |
215 | rcu_read_unlock(); |
216 | *key = ILLEGAL_REQUEST; |
217 | *asc = 0x25; /* LOGICAL UNIT NOT SUPPORTED */ |
218 | *ascq = 0; |
219 | return true; |
220 | } |
221 | *key = UNIT_ATTENTION; |
222 | /* |
223 | * The highest priority Unit Attentions are placed at the head of the |
224 | * struct se_dev_entry->ua_list, and will be returned in CHECK_CONDITION + |
225 | * sense data for the received CDB. |
226 | */ |
227 | spin_lock(lock: &deve->ua_lock); |
228 | list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) { |
229 | /* |
230 | * For ua_intlck_ctrl code not equal to 00b, only report the |
231 | * highest priority UNIT_ATTENTION and ASC/ASCQ without |
232 | * clearing it. |
233 | */ |
234 | if (!dev_ua_intlck_clear) { |
235 | *asc = ua->ua_asc; |
236 | *ascq = ua->ua_ascq; |
237 | break; |
238 | } |
239 | /* |
240 | * Otherwise for the default 00b, release the UNIT ATTENTION |
241 | * condition. Return the ASC/ASCQ of the highest priority UA |
242 | * (head of the list) in the outgoing CHECK_CONDITION + sense. |
243 | */ |
244 | if (head) { |
245 | *asc = ua->ua_asc; |
246 | *ascq = ua->ua_ascq; |
247 | head = 0; |
248 | } |
249 | list_del(entry: &ua->ua_nacl_list); |
250 | kmem_cache_free(s: se_ua_cache, objp: ua); |
251 | } |
252 | spin_unlock(lock: &deve->ua_lock); |
253 | rcu_read_unlock(); |
254 | |
255 | pr_debug("[%s]: %s UNIT ATTENTION condition with" |
256 | " INTLCK_CTRL: %d, mapped LUN: %llu, got CDB: 0x%02x" |
257 | " reported ASC: 0x%02x, ASCQ: 0x%02x\n" , |
258 | nacl->se_tpg->se_tpg_tfo->fabric_name, |
259 | dev_ua_intlck_clear ? "Releasing" : "Reporting" , |
260 | dev->dev_attrib.emulate_ua_intlck_ctrl, |
261 | cmd->orig_fe_lun, cmd->t_task_cdb[0], *asc, *ascq); |
262 | |
263 | return head == 0; |
264 | } |
265 | |
266 | int core_scsi3_ua_clear_for_request_sense( |
267 | struct se_cmd *cmd, |
268 | u8 *asc, |
269 | u8 *ascq) |
270 | { |
271 | struct se_dev_entry *deve; |
272 | struct se_session *sess = cmd->se_sess; |
273 | struct se_node_acl *nacl; |
274 | struct se_ua *ua = NULL, *ua_p; |
275 | int head = 1; |
276 | |
277 | if (!sess) |
278 | return -EINVAL; |
279 | |
280 | nacl = sess->se_node_acl; |
281 | if (!nacl) |
282 | return -EINVAL; |
283 | |
284 | rcu_read_lock(); |
285 | deve = target_nacl_find_deve(nacl, cmd->orig_fe_lun); |
286 | if (!deve) { |
287 | rcu_read_unlock(); |
288 | return -EINVAL; |
289 | } |
290 | if (list_empty_careful(head: &deve->ua_list)) { |
291 | rcu_read_unlock(); |
292 | return -EPERM; |
293 | } |
294 | /* |
295 | * The highest priority Unit Attentions are placed at the head of the |
296 | * struct se_dev_entry->ua_list. The First (and hence highest priority) |
297 | * ASC/ASCQ will be returned in REQUEST_SENSE payload data for the |
298 | * matching struct se_lun. |
299 | * |
300 | * Once the returning ASC/ASCQ values are set, we go ahead and |
301 | * release all of the Unit Attention conditions for the associated |
302 | * struct se_lun. |
303 | */ |
304 | spin_lock(lock: &deve->ua_lock); |
305 | list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) { |
306 | if (head) { |
307 | *asc = ua->ua_asc; |
308 | *ascq = ua->ua_ascq; |
309 | head = 0; |
310 | } |
311 | list_del(entry: &ua->ua_nacl_list); |
312 | kmem_cache_free(s: se_ua_cache, objp: ua); |
313 | } |
314 | spin_unlock(lock: &deve->ua_lock); |
315 | rcu_read_unlock(); |
316 | |
317 | pr_debug("[%s]: Released UNIT ATTENTION condition, mapped" |
318 | " LUN: %llu, got REQUEST_SENSE reported ASC: 0x%02x," |
319 | " ASCQ: 0x%02x\n" , nacl->se_tpg->se_tpg_tfo->fabric_name, |
320 | cmd->orig_fe_lun, *asc, *ascq); |
321 | |
322 | return (head) ? -EPERM : 0; |
323 | } |
324 | |