Commit | Line | Data |
---|---|---|
2908d778 JB |
1 | /* |
2 | * Serial Attached SCSI (SAS) Transport Layer initialization | |
3 | * | |
4 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. | |
5 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> | |
6 | * | |
7 | * This file is licensed under GPLv2. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation; either version 2 of the | |
12 | * License, or (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
23 | * | |
24 | */ | |
25 | ||
26 | #include <linux/module.h> | |
5a0e3ad6 | 27 | #include <linux/slab.h> |
2908d778 JB |
28 | #include <linux/init.h> |
29 | #include <linux/device.h> | |
30 | #include <linux/spinlock.h> | |
81c757bc | 31 | #include <scsi/sas_ata.h> |
2908d778 JB |
32 | #include <scsi/scsi_host.h> |
33 | #include <scsi/scsi_device.h> | |
34 | #include <scsi/scsi_transport.h> | |
35 | #include <scsi/scsi_transport_sas.h> | |
36 | ||
37 | #include "sas_internal.h" | |
38 | ||
39 | #include "../scsi_sas_internal.h" | |
40 | ||
4fcf812c DW |
41 | static struct kmem_cache *sas_task_cache; |
42 | ||
43 | struct sas_task *sas_alloc_task(gfp_t flags) | |
44 | { | |
45 | struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags); | |
46 | ||
47 | if (task) { | |
48 | INIT_LIST_HEAD(&task->list); | |
49 | spin_lock_init(&task->task_state_lock); | |
50 | task->task_state_flags = SAS_TASK_STATE_PENDING; | |
51 | init_timer(&task->timer); | |
52 | init_completion(&task->completion); | |
53 | } | |
54 | ||
55 | return task; | |
56 | } | |
57 | EXPORT_SYMBOL_GPL(sas_alloc_task); | |
58 | ||
59 | void sas_free_task(struct sas_task *task) | |
60 | { | |
61 | if (task) { | |
62 | BUG_ON(!list_empty(&task->list)); | |
63 | kmem_cache_free(sas_task_cache, task); | |
64 | } | |
65 | } | |
66 | EXPORT_SYMBOL_GPL(sas_free_task); | |
2908d778 JB |
67 | |
68 | /*------------ SAS addr hash -----------*/ | |
69 | void sas_hash_addr(u8 *hashed, const u8 *sas_addr) | |
70 | { | |
71 | const u32 poly = 0x00DB2777; | |
72 | u32 r = 0; | |
73 | int i; | |
74 | ||
75 | for (i = 0; i < 8; i++) { | |
76 | int b; | |
77 | for (b = 7; b >= 0; b--) { | |
78 | r <<= 1; | |
79 | if ((1 << b) & sas_addr[i]) { | |
80 | if (!(r & 0x01000000)) | |
81 | r ^= poly; | |
82 | } else if (r & 0x01000000) | |
83 | r ^= poly; | |
84 | } | |
85 | } | |
86 | ||
87 | hashed[0] = (r >> 16) & 0xFF; | |
88 | hashed[1] = (r >> 8) & 0xFF ; | |
89 | hashed[2] = r & 0xFF; | |
90 | } | |
91 | ||
92 | ||
93 | /* ---------- HA events ---------- */ | |
94 | ||
c4028958 | 95 | void sas_hae_reset(struct work_struct *work) |
2908d778 | 96 | { |
22b9153f | 97 | struct sas_ha_event *ev = to_sas_ha_event(work); |
c4028958 | 98 | struct sas_ha_struct *ha = ev->ha; |
2908d778 | 99 | |
b15ebe0b | 100 | clear_bit(HAE_RESET, &ha->pending); |
2908d778 JB |
101 | } |
102 | ||
103 | int sas_register_ha(struct sas_ha_struct *sas_ha) | |
104 | { | |
105 | int error = 0; | |
106 | ||
87c8331f | 107 | mutex_init(&sas_ha->disco_mutex); |
2908d778 JB |
108 | spin_lock_init(&sas_ha->phy_port_lock); |
109 | sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr); | |
110 | ||
111 | if (sas_ha->lldd_queue_size == 0) | |
112 | sas_ha->lldd_queue_size = 1; | |
113 | else if (sas_ha->lldd_queue_size == -1) | |
114 | sas_ha->lldd_queue_size = 128; /* Sanity */ | |
115 | ||
f8daa6e6 | 116 | set_bit(SAS_HA_REGISTERED, &sas_ha->state); |
e4a9c373 | 117 | spin_lock_init(&sas_ha->lock); |
b1124cd3 | 118 | mutex_init(&sas_ha->drain_mutex); |
5db45bdc | 119 | init_waitqueue_head(&sas_ha->eh_wait_q); |
b1124cd3 | 120 | INIT_LIST_HEAD(&sas_ha->defer_q); |
5db45bdc | 121 | INIT_LIST_HEAD(&sas_ha->eh_dev_q); |
6b0efb85 | 122 | |
2908d778 JB |
123 | error = sas_register_phys(sas_ha); |
124 | if (error) { | |
125 | printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); | |
126 | return error; | |
127 | } | |
128 | ||
129 | error = sas_register_ports(sas_ha); | |
130 | if (error) { | |
131 | printk(KERN_NOTICE "couldn't register sas ports:%d\n", error); | |
132 | goto Undo_phys; | |
133 | } | |
134 | ||
135 | error = sas_init_events(sas_ha); | |
136 | if (error) { | |
137 | printk(KERN_NOTICE "couldn't start event thread:%d\n", error); | |
138 | goto Undo_ports; | |
139 | } | |
140 | ||
141 | if (sas_ha->lldd_max_execute_num > 1) { | |
142 | error = sas_init_queue(sas_ha); | |
143 | if (error) { | |
144 | printk(KERN_NOTICE "couldn't start queue thread:%d, " | |
145 | "running in direct mode\n", error); | |
146 | sas_ha->lldd_max_execute_num = 1; | |
147 | } | |
148 | } | |
149 | ||
f456393e | 150 | INIT_LIST_HEAD(&sas_ha->eh_done_q); |
3944f509 | 151 | INIT_LIST_HEAD(&sas_ha->eh_ata_q); |
f456393e | 152 | |
2908d778 JB |
153 | return 0; |
154 | ||
155 | Undo_ports: | |
156 | sas_unregister_ports(sas_ha); | |
157 | Undo_phys: | |
158 | ||
159 | return error; | |
160 | } | |
161 | ||
162 | int sas_unregister_ha(struct sas_ha_struct *sas_ha) | |
163 | { | |
b1124cd3 | 164 | /* Set the state to unregistered to avoid further unchained |
5d7f6d10 | 165 | * events to be queued, and flush any in-progress drainers |
b1124cd3 | 166 | */ |
5d7f6d10 | 167 | mutex_lock(&sas_ha->drain_mutex); |
e4a9c373 | 168 | spin_lock_irq(&sas_ha->lock); |
f8daa6e6 | 169 | clear_bit(SAS_HA_REGISTERED, &sas_ha->state); |
e4a9c373 | 170 | spin_unlock_irq(&sas_ha->lock); |
5d7f6d10 DW |
171 | __sas_drain_work(sas_ha); |
172 | mutex_unlock(&sas_ha->drain_mutex); | |
6b0efb85 | 173 | |
cde3f74b | 174 | sas_unregister_ports(sas_ha); |
5d7f6d10 DW |
175 | |
176 | /* flush unregistration work */ | |
177 | mutex_lock(&sas_ha->drain_mutex); | |
178 | __sas_drain_work(sas_ha); | |
179 | mutex_unlock(&sas_ha->drain_mutex); | |
cde3f74b | 180 | |
2908d778 JB |
181 | if (sas_ha->lldd_max_execute_num > 1) { |
182 | sas_shutdown_queue(sas_ha); | |
cde3f74b | 183 | sas_ha->lldd_max_execute_num = 1; |
2908d778 JB |
184 | } |
185 | ||
2908d778 JB |
186 | return 0; |
187 | } | |
188 | ||
189 | static int sas_get_linkerrors(struct sas_phy *phy) | |
190 | { | |
ac013ed1 DW |
191 | if (scsi_is_sas_phy_local(phy)) { |
192 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
193 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | |
194 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | |
195 | struct sas_internal *i = | |
196 | to_sas_internal(sas_ha->core.shost->transportt); | |
197 | ||
198 | return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); | |
199 | } | |
2908d778 JB |
200 | |
201 | return sas_smp_get_phy_events(phy); | |
202 | } | |
203 | ||
ab526633 DW |
204 | int sas_try_ata_reset(struct asd_sas_phy *asd_phy) |
205 | { | |
206 | struct domain_device *dev = NULL; | |
207 | ||
208 | /* try to route user requested link resets through libata */ | |
209 | if (asd_phy->port) | |
210 | dev = asd_phy->port->port_dev; | |
211 | ||
212 | /* validate that dev has been probed */ | |
213 | if (dev) | |
214 | dev = sas_find_dev_by_rphy(dev->rphy); | |
215 | ||
216 | if (dev && dev_is_sata(dev)) { | |
217 | sas_ata_schedule_reset(dev); | |
218 | sas_ata_wait_eh(dev); | |
219 | return 0; | |
220 | } | |
221 | ||
222 | return -ENODEV; | |
223 | } | |
224 | ||
81c757bc DW |
225 | /** |
226 | * transport_sas_phy_reset - reset a phy and permit libata to manage the link | |
227 | * | |
228 | * phy reset request via sysfs in host workqueue context so we know we | |
229 | * can block on eh and safely traverse the domain_device topology | |
230 | */ | |
231 | static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) | |
232 | { | |
81c757bc DW |
233 | enum phy_func reset_type; |
234 | ||
235 | if (hard_reset) | |
236 | reset_type = PHY_FUNC_HARD_RESET; | |
237 | else | |
238 | reset_type = PHY_FUNC_LINK_RESET; | |
239 | ||
240 | if (scsi_is_sas_phy_local(phy)) { | |
241 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
242 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | |
243 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | |
244 | struct sas_internal *i = | |
245 | to_sas_internal(sas_ha->core.shost->transportt); | |
81c757bc | 246 | |
ab526633 DW |
247 | if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) |
248 | return 0; | |
249 | return i->dft->lldd_control_phy(asd_phy, reset_type, NULL); | |
81c757bc DW |
250 | } else { |
251 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); | |
252 | struct domain_device *ddev = sas_find_dev_by_rphy(rphy); | |
253 | struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number); | |
254 | ||
255 | if (ata_dev && !hard_reset) { | |
256 | sas_ata_schedule_reset(ata_dev); | |
257 | sas_ata_wait_eh(ata_dev); | |
ab526633 | 258 | return 0; |
81c757bc | 259 | } else |
ab526633 | 260 | return sas_smp_phy_control(ddev, phy->number, reset_type, NULL); |
81c757bc | 261 | } |
81c757bc DW |
262 | } |
263 | ||
2a559f4b | 264 | static int sas_phy_enable(struct sas_phy *phy, int enable) |
acbf167d DW |
265 | { |
266 | int ret; | |
2a559f4b | 267 | enum phy_func cmd; |
acbf167d DW |
268 | |
269 | if (enable) | |
2a559f4b | 270 | cmd = PHY_FUNC_LINK_RESET; |
acbf167d | 271 | else |
2a559f4b | 272 | cmd = PHY_FUNC_DISABLE; |
acbf167d DW |
273 | |
274 | if (scsi_is_sas_phy_local(phy)) { | |
275 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
276 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | |
277 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | |
278 | struct sas_internal *i = | |
279 | to_sas_internal(sas_ha->core.shost->transportt); | |
280 | ||
2a559f4b DW |
281 | if (enable) |
282 | ret = transport_sas_phy_reset(phy, 0); | |
1f4fe89c | 283 | else |
2a559f4b | 284 | ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL); |
acbf167d DW |
285 | } else { |
286 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); | |
287 | struct domain_device *ddev = sas_find_dev_by_rphy(rphy); | |
2a559f4b DW |
288 | |
289 | if (enable) | |
290 | ret = transport_sas_phy_reset(phy, 0); | |
291 | else | |
292 | ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL); | |
acbf167d DW |
293 | } |
294 | return ret; | |
295 | } | |
296 | ||
dea22214 | 297 | int sas_phy_reset(struct sas_phy *phy, int hard_reset) |
2908d778 JB |
298 | { |
299 | int ret; | |
300 | enum phy_func reset_type; | |
301 | ||
26a2e68f DW |
302 | if (!phy->enabled) |
303 | return -ENODEV; | |
304 | ||
2908d778 JB |
305 | if (hard_reset) |
306 | reset_type = PHY_FUNC_HARD_RESET; | |
307 | else | |
308 | reset_type = PHY_FUNC_LINK_RESET; | |
309 | ||
310 | if (scsi_is_sas_phy_local(phy)) { | |
311 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
312 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | |
313 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | |
314 | struct sas_internal *i = | |
315 | to_sas_internal(sas_ha->core.shost->transportt); | |
316 | ||
a01e70e5 | 317 | ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); |
2908d778 JB |
318 | } else { |
319 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); | |
320 | struct domain_device *ddev = sas_find_dev_by_rphy(rphy); | |
a01e70e5 | 321 | ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL); |
2908d778 JB |
322 | } |
323 | return ret; | |
324 | } | |
325 | ||
acbf167d DW |
326 | int sas_set_phy_speed(struct sas_phy *phy, |
327 | struct sas_phy_linkrates *rates) | |
a01e70e5 JB |
328 | { |
329 | int ret; | |
330 | ||
331 | if ((rates->minimum_linkrate && | |
332 | rates->minimum_linkrate > phy->maximum_linkrate) || | |
333 | (rates->maximum_linkrate && | |
334 | rates->maximum_linkrate < phy->minimum_linkrate)) | |
335 | return -EINVAL; | |
336 | ||
337 | if (rates->minimum_linkrate && | |
338 | rates->minimum_linkrate < phy->minimum_linkrate_hw) | |
339 | rates->minimum_linkrate = phy->minimum_linkrate_hw; | |
340 | ||
341 | if (rates->maximum_linkrate && | |
342 | rates->maximum_linkrate > phy->maximum_linkrate_hw) | |
343 | rates->maximum_linkrate = phy->maximum_linkrate_hw; | |
344 | ||
345 | if (scsi_is_sas_phy_local(phy)) { | |
346 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
347 | struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); | |
348 | struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; | |
349 | struct sas_internal *i = | |
350 | to_sas_internal(sas_ha->core.shost->transportt); | |
351 | ||
352 | ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, | |
353 | rates); | |
354 | } else { | |
355 | struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); | |
356 | struct domain_device *ddev = sas_find_dev_by_rphy(rphy); | |
357 | ret = sas_smp_phy_control(ddev, phy->number, | |
358 | PHY_FUNC_LINK_RESET, rates); | |
359 | ||
360 | } | |
361 | ||
362 | return ret; | |
363 | } | |
364 | ||
0b3e09da DW |
365 | static void sas_phy_release(struct sas_phy *phy) |
366 | { | |
367 | kfree(phy->hostdata); | |
368 | phy->hostdata = NULL; | |
369 | } | |
370 | ||
371 | static void phy_reset_work(struct work_struct *work) | |
372 | { | |
22b9153f | 373 | struct sas_phy_data *d = container_of(work, typeof(*d), reset_work.work); |
0b3e09da | 374 | |
81c757bc | 375 | d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset); |
0b3e09da DW |
376 | } |
377 | ||
2a559f4b DW |
378 | static void phy_enable_work(struct work_struct *work) |
379 | { | |
22b9153f | 380 | struct sas_phy_data *d = container_of(work, typeof(*d), enable_work.work); |
2a559f4b DW |
381 | |
382 | d->enable_result = sas_phy_enable(d->phy, d->enable); | |
383 | } | |
384 | ||
0b3e09da DW |
385 | static int sas_phy_setup(struct sas_phy *phy) |
386 | { | |
387 | struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); | |
388 | ||
389 | if (!d) | |
390 | return -ENOMEM; | |
391 | ||
392 | mutex_init(&d->event_lock); | |
22b9153f DW |
393 | INIT_SAS_WORK(&d->reset_work, phy_reset_work); |
394 | INIT_SAS_WORK(&d->enable_work, phy_enable_work); | |
0b3e09da DW |
395 | d->phy = phy; |
396 | phy->hostdata = d; | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
401 | static int queue_phy_reset(struct sas_phy *phy, int hard_reset) | |
402 | { | |
403 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
404 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | |
405 | struct sas_phy_data *d = phy->hostdata; | |
406 | int rc; | |
407 | ||
408 | if (!d) | |
409 | return -ENOMEM; | |
410 | ||
411 | /* libsas workqueue coordinates ata-eh reset with discovery */ | |
412 | mutex_lock(&d->event_lock); | |
413 | d->reset_result = 0; | |
414 | d->hard_reset = hard_reset; | |
415 | ||
e4a9c373 | 416 | spin_lock_irq(&ha->lock); |
0b3e09da | 417 | sas_queue_work(ha, &d->reset_work); |
e4a9c373 | 418 | spin_unlock_irq(&ha->lock); |
0b3e09da DW |
419 | |
420 | rc = sas_drain_work(ha); | |
421 | if (rc == 0) | |
422 | rc = d->reset_result; | |
423 | mutex_unlock(&d->event_lock); | |
424 | ||
425 | return rc; | |
426 | } | |
427 | ||
2a559f4b DW |
428 | static int queue_phy_enable(struct sas_phy *phy, int enable) |
429 | { | |
430 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | |
431 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | |
432 | struct sas_phy_data *d = phy->hostdata; | |
433 | int rc; | |
434 | ||
435 | if (!d) | |
436 | return -ENOMEM; | |
437 | ||
438 | /* libsas workqueue coordinates ata-eh reset with discovery */ | |
439 | mutex_lock(&d->event_lock); | |
440 | d->enable_result = 0; | |
441 | d->enable = enable; | |
442 | ||
e4a9c373 | 443 | spin_lock_irq(&ha->lock); |
2a559f4b | 444 | sas_queue_work(ha, &d->enable_work); |
e4a9c373 | 445 | spin_unlock_irq(&ha->lock); |
2a559f4b DW |
446 | |
447 | rc = sas_drain_work(ha); | |
448 | if (rc == 0) | |
449 | rc = d->enable_result; | |
450 | mutex_unlock(&d->event_lock); | |
451 | ||
452 | return rc; | |
453 | } | |
454 | ||
2908d778 | 455 | static struct sas_function_template sft = { |
2a559f4b | 456 | .phy_enable = queue_phy_enable, |
0b3e09da DW |
457 | .phy_reset = queue_phy_reset, |
458 | .phy_setup = sas_phy_setup, | |
459 | .phy_release = sas_phy_release, | |
a01e70e5 | 460 | .set_phy_speed = sas_set_phy_speed, |
2908d778 | 461 | .get_linkerrors = sas_get_linkerrors, |
ba1fc175 | 462 | .smp_handler = sas_smp_handler, |
2908d778 JB |
463 | }; |
464 | ||
465 | struct scsi_transport_template * | |
466 | sas_domain_attach_transport(struct sas_domain_function_template *dft) | |
467 | { | |
468 | struct scsi_transport_template *stt = sas_attach_transport(&sft); | |
469 | struct sas_internal *i; | |
470 | ||
471 | if (!stt) | |
472 | return stt; | |
473 | ||
474 | i = to_sas_internal(stt); | |
475 | i->dft = dft; | |
476 | stt->create_work_queue = 1; | |
477 | stt->eh_timed_out = sas_scsi_timed_out; | |
478 | stt->eh_strategy_handler = sas_scsi_recover_host; | |
479 | ||
480 | return stt; | |
481 | } | |
482 | EXPORT_SYMBOL_GPL(sas_domain_attach_transport); | |
483 | ||
484 | ||
485 | void sas_domain_release_transport(struct scsi_transport_template *stt) | |
486 | { | |
487 | sas_release_transport(stt); | |
488 | } | |
489 | EXPORT_SYMBOL_GPL(sas_domain_release_transport); | |
490 | ||
491 | /* ---------- SAS Class register/unregister ---------- */ | |
492 | ||
493 | static int __init sas_class_init(void) | |
494 | { | |
4fcf812c | 495 | sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN); |
2908d778 JB |
496 | if (!sas_task_cache) |
497 | return -ENOMEM; | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static void __exit sas_class_exit(void) | |
503 | { | |
504 | kmem_cache_destroy(sas_task_cache); | |
505 | } | |
506 | ||
507 | MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>"); | |
508 | MODULE_DESCRIPTION("SAS Transport Layer"); | |
509 | MODULE_LICENSE("GPL v2"); | |
510 | ||
511 | module_init(sas_class_init); | |
512 | module_exit(sas_class_exit); | |
513 | ||
514 | EXPORT_SYMBOL_GPL(sas_register_ha); | |
515 | EXPORT_SYMBOL_GPL(sas_unregister_ha); |