This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / scsi / scsi_transport_fc.c
1 /* 
2  *  FiberChannel transport specific attributes exported to sysfs.
3  *
4  *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <scsi/scsi_device.h>
23 #include <scsi/scsi_host.h>
24 #include <scsi/scsi_transport.h>
25 #include <scsi/scsi_transport_fc.h>
26 #include "scsi_priv.h"
27
28 #define FC_PRINTK(x, l, f, a...)        printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)
29
30 static void transport_class_release(struct class_device *class_dev);
31 static void host_class_release(struct class_device *class_dev);
32 static void fc_timeout_blocked_host(void *data);
33 static void fc_timeout_blocked_tgt(void *data);
34
35 #define FC_STARGET_NUM_ATTRS    4       /* increase this if you add attributes */
36 #define FC_STARGET_OTHER_ATTRS  0       /* increase this if you add "always on"
37                                          * attributes */
38 #define FC_HOST_NUM_ATTRS       1
39
40 struct fc_internal {
41         struct scsi_transport_template t;
42         struct fc_function_template *f;
43         /* The actual attributes */
44         struct class_device_attribute private_starget_attrs[
45                                                 FC_STARGET_NUM_ATTRS];
46         /* The array of null terminated pointers to attributes
47          * needed by scsi_sysfs.c */
48         struct class_device_attribute *starget_attrs[
49                         FC_STARGET_NUM_ATTRS + FC_STARGET_OTHER_ATTRS + 1];
50
51         struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
52         struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];
53 };
54
55 #define to_fc_internal(tmpl)    container_of(tmpl, struct fc_internal, t)
56
57 struct class fc_transport_class = {
58         .name = "fc_transport",
59         .release = transport_class_release,
60 };
61
62 struct class fc_host_class = {
63         .name = "fc_host",
64         .release = host_class_release,
65 };
66
67 static __init int fc_transport_init(void)
68 {
69         int error = class_register(&fc_host_class);
70         if (error)
71                 return error;
72         return class_register(&fc_transport_class);
73 }
74
75 static void __exit fc_transport_exit(void)
76 {
77         class_unregister(&fc_transport_class);
78         class_unregister(&fc_host_class);
79 }
80
81 static int fc_setup_starget_transport_attrs(struct scsi_target *starget)
82 {
83         /* 
84          * Set default values easily detected by the midlayer as
85          * failure cases.  The scsi lldd is responsible for initializing
86          * all transport attributes to valid values per target.
87          */
88         fc_starget_node_name(starget) = -1;
89         fc_starget_port_name(starget) = -1;
90         fc_starget_port_id(starget) = -1;
91         fc_starget_dev_loss_tmo(starget) = -1;
92         INIT_WORK(&fc_starget_dev_loss_work(starget),
93                   fc_timeout_blocked_tgt, starget);
94         return 0;
95 }
96
97 static void fc_destroy_starget(struct scsi_target *starget)
98 {
99         /* Stop the target timer */
100         if (cancel_delayed_work(&fc_starget_dev_loss_work(starget)))
101                 flush_scheduled_work();
102 }
103
104 static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
105 {
106         /* 
107          * Set default values easily detected by the midlayer as
108          * failure cases.  The scsi lldd is responsible for initializing
109          * all transport attributes to valid values per host.
110          */
111         fc_host_link_down_tmo(shost) = -1;
112         INIT_WORK(&fc_host_link_down_work(shost),
113                   fc_timeout_blocked_host, shost);
114         return 0;
115 }
116
117 static void fc_destroy_host(struct Scsi_Host *shost)
118 {
119         /* Stop the host timer */
120         if (cancel_delayed_work(&fc_host_link_down_work(shost)))
121                 flush_scheduled_work();
122 }
123
124 static void transport_class_release(struct class_device *class_dev)
125 {
126         struct scsi_target *starget = transport_class_to_starget(class_dev);
127         put_device(&starget->dev);
128 }
129
130 static void host_class_release(struct class_device *class_dev)
131 {
132         struct Scsi_Host *shost = transport_class_to_shost(class_dev);
133         put_device(&shost->shost_gendev);
134 }
135
136
137 /*
138  * Remote Port Attribute Management
139  */
140
141 #define fc_starget_show_function(field, format_string, cast)            \
142 static ssize_t                                                          \
143 show_fc_starget_##field (struct class_device *cdev, char *buf)          \
144 {                                                                       \
145         struct scsi_target *starget = transport_class_to_starget(cdev); \
146         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
147         struct fc_starget_attrs *tp;                                    \
148         struct fc_internal *i = to_fc_internal(shost->transportt);      \
149         tp = (struct fc_starget_attrs *)&starget->starget_data;         \
150         if (i->f->get_starget_##field)                                  \
151                 i->f->get_starget_##field(starget);                     \
152         return snprintf(buf, 20, format_string, cast tp->field);        \
153 }
154
155 #define fc_starget_store_function(field, format_string)                 \
156 static ssize_t                                                          \
157 store_fc_starget_##field(struct class_device *cdev, const char *buf,    \
158                            size_t count)                                \
159 {                                                                       \
160         int val;                                                        \
161         struct scsi_target *starget = transport_class_to_starget(cdev); \
162         struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);    \
163         struct fc_internal *i = to_fc_internal(shost->transportt);      \
164                                                                         \
165         val = simple_strtoul(buf, NULL, 0);                             \
166         i->f->set_starget_##field(starget, val);                        \
167         return count;                                                   \
168 }
169
170 #define fc_starget_rd_attr(field, format_string)                        \
171         fc_starget_show_function(field, format_string, )                \
172 static CLASS_DEVICE_ATTR(field, S_IRUGO,                                \
173                          show_fc_starget_##field, NULL)
174
175 #define fc_starget_rd_attr_cast(field, format_string, cast)             \
176         fc_starget_show_function(field, format_string, (cast))          \
177 static CLASS_DEVICE_ATTR(field, S_IRUGO,                                \
178                           show_fc_starget_##field, NULL)
179
180 #define fc_starget_rw_attr(field, format_string)                        \
181         fc_starget_show_function(field, format_string, )                \
182         fc_starget_store_function(field, format_string)                 \
183 static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR,                      \
184                         show_fc_starget_##field,                        \
185                         store_fc_starget_##field)
186
187 #define SETUP_STARGET_ATTRIBUTE_RD(field)                               \
188         i->private_starget_attrs[count] = class_device_attr_##field;    \
189         i->private_starget_attrs[count].attr.mode = S_IRUGO;            \
190         i->private_starget_attrs[count].store = NULL;                   \
191         i->starget_attrs[count] = &i->private_starget_attrs[count];     \
192         if (i->f->show_starget_##field)                                 \
193                 count++
194
195 #define SETUP_STARGET_ATTRIBUTE_RW(field)                               \
196         i->private_starget_attrs[count] = class_device_attr_##field;    \
197         if (!i->f->set_starget_##field) {                               \
198                 i->private_starget_attrs[count].attr.mode = S_IRUGO;    \
199                 i->private_starget_attrs[count].store = NULL;           \
200         }                                                               \
201         i->starget_attrs[count] = &i->private_starget_attrs[count];     \
202         if (i->f->show_starget_##field)                                 \
203                 count++
204
205 /* The FC Tranport Remote Port (Target) Attributes: */
206 fc_starget_rd_attr_cast(node_name, "0x%llx\n", unsigned long long);
207 fc_starget_rd_attr_cast(port_name, "0x%llx\n", unsigned long long);
208 fc_starget_rd_attr(port_id, "0x%06x\n");
209 fc_starget_rw_attr(dev_loss_tmo, "%d\n");
210
211
212 /*
213  * Host Attribute Management
214  */
215
216 #define fc_host_show_function(field, format_string, cast)               \
217 static ssize_t                                                          \
218 show_fc_host_##field (struct class_device *cdev, char *buf)             \
219 {                                                                       \
220         struct Scsi_Host *shost = transport_class_to_shost(cdev);       \
221         struct fc_host_attrs *tp;                                       \
222         struct fc_internal *i = to_fc_internal(shost->transportt);      \
223         tp = (struct fc_host_attrs *)shost->shost_data;         \
224         if (i->f->get_host_##field)                                     \
225                 i->f->get_host_##field(shost);                          \
226         return snprintf(buf, 20, format_string, cast tp->field);        \
227 }
228
229 #define fc_host_store_function(field, format_string)                    \
230 static ssize_t                                                          \
231 store_fc_host_##field(struct class_device *cdev, const char *buf,       \
232                            size_t count)                                \
233 {                                                                       \
234         int val;                                                        \
235         struct Scsi_Host *shost = transport_class_to_shost(cdev);       \
236         struct fc_internal *i = to_fc_internal(shost->transportt);      \
237                                                                         \
238         val = simple_strtoul(buf, NULL, 0);                             \
239         i->f->set_host_##field(shost, val);                             \
240         return count;                                                   \
241 }
242
243 #define fc_host_rd_attr(field, format_string)                           \
244         fc_host_show_function(field, format_string, )                   \
245 static CLASS_DEVICE_ATTR(host_##field, S_IRUGO,                         \
246                          show_fc_host_##field, NULL)
247
248 #define fc_host_rd_attr_cast(field, format_string, cast)                \
249         fc_host_show_function(field, format_string, (cast))             \
250 static CLASS_DEVICE_ATTR(host_##field, S_IRUGO,                         \
251                           show_fc_host_##field, NULL)
252
253 #define fc_host_rw_attr(field, format_string)                           \
254         fc_host_show_function(field, format_string, )                   \
255         fc_host_store_function(field, format_string)                    \
256 static CLASS_DEVICE_ATTR(host_##field, S_IRUGO | S_IWUSR,               \
257                         show_fc_host_##field,                           \
258                         store_fc_host_##field)
259
260 #define SETUP_HOST_ATTRIBUTE_RD(field)                                  \
261         i->private_host_attrs[count] = class_device_attr_host_##field;  \
262         i->private_host_attrs[count].attr.mode = S_IRUGO;               \
263         i->private_host_attrs[count].store = NULL;                      \
264         i->host_attrs[count] = &i->private_host_attrs[count];           \
265         if (i->f->show_host_##field)                                    \
266                 count++
267
268 #define SETUP_HOST_ATTRIBUTE_RW(field)                                  \
269         i->private_host_attrs[count] = class_device_attr_host_##field;  \
270         if (!i->f->set_host_##field) {                                  \
271                 i->private_host_attrs[count].attr.mode = S_IRUGO;       \
272                 i->private_host_attrs[count].store = NULL;              \
273         }                                                               \
274         i->host_attrs[count] = &i->private_host_attrs[count];           \
275         if (i->f->show_host_##field)                                    \
276                 count++
277
278 /* The FC Tranport Host Attributes: */
279 fc_host_rw_attr(link_down_tmo, "%d\n");
280
281
282
283 struct scsi_transport_template *
284 fc_attach_transport(struct fc_function_template *ft)
285 {
286         struct fc_internal *i = kmalloc(sizeof(struct fc_internal),
287                                         GFP_KERNEL);
288         int count = 0;
289
290         if (unlikely(!i))
291                 return NULL;
292
293         memset(i, 0, sizeof(struct fc_internal));
294
295         i->t.target_attrs = &i->starget_attrs[0];
296         i->t.target_class = &fc_transport_class;
297         i->t.target_setup = &fc_setup_starget_transport_attrs;
298         i->t.target_destroy = &fc_destroy_starget;
299         i->t.target_size = sizeof(struct fc_starget_attrs);
300
301         i->t.host_attrs = &i->host_attrs[0];
302         i->t.host_class = &fc_host_class;
303         i->t.host_setup = &fc_setup_host_transport_attrs;
304         i->t.host_destroy = &fc_destroy_host;
305         i->t.host_size = sizeof(struct fc_host_attrs);
306         i->f = ft;
307
308         
309         /*
310          * setup remote port (target) attributes
311          */
312         SETUP_STARGET_ATTRIBUTE_RD(port_id);
313         SETUP_STARGET_ATTRIBUTE_RD(port_name);
314         SETUP_STARGET_ATTRIBUTE_RD(node_name);
315         SETUP_STARGET_ATTRIBUTE_RW(dev_loss_tmo);
316
317         BUG_ON(count > FC_STARGET_NUM_ATTRS);
318
319         /* Setup the always-on attributes here */
320
321         i->starget_attrs[count] = NULL;
322
323
324         /* setup host attributes */
325         count=0;
326         SETUP_HOST_ATTRIBUTE_RW(link_down_tmo);
327
328         BUG_ON(count > FC_HOST_NUM_ATTRS);
329
330         /* Setup the always-on attributes here */
331
332         i->host_attrs[count] = NULL;
333
334
335         return &i->t;
336 }
337 EXPORT_SYMBOL(fc_attach_transport);
338
339 void fc_release_transport(struct scsi_transport_template *t)
340 {
341         struct fc_internal *i = to_fc_internal(t);
342
343         kfree(i);
344 }
345 EXPORT_SYMBOL(fc_release_transport);
346
347
348
349 /**
350  * fc_device_block - called by target functions to block a scsi device
351  * @dev:        scsi device
352  * @data:       unused
353  **/
354 static int fc_device_block(struct device *dev, void *data)
355 {
356         scsi_internal_device_block(to_scsi_device(dev));
357         return 0;
358 }
359
360 /**
361  * fc_device_unblock - called by target functions to unblock a scsi device
362  * @dev:        scsi device
363  * @data:       unused
364  **/
365 static int fc_device_unblock(struct device *dev, void *data)
366 {
367         scsi_internal_device_unblock(to_scsi_device(dev));
368         return 0;
369 }
370
371 /**
372  * fc_timeout_blocked_tgt - Timeout handler for blocked scsi targets
373  *                       that fail to recover in the alloted time.
374  * @data:       scsi target that failed to reappear in the alloted time.
375  **/
376 static void fc_timeout_blocked_tgt(void  *data)
377 {
378         struct scsi_target *starget = (struct scsi_target *)data;
379
380         dev_printk(KERN_ERR, &starget->dev, 
381                 "blocked target time out: target resuming\n");
382
383         /* 
384          * set the device going again ... if the scsi lld didn't
385          * unblock this device, then IO errors will probably
386          * result if the host still isn't ready.
387          */
388         device_for_each_child(&starget->dev, NULL, fc_device_unblock);
389 }
390
391 /**
392  * fc_target_block - block a target by temporarily putting all its scsi devices
393  *              into the SDEV_BLOCK state.
394  * @starget:    scsi target managed by this fc scsi lldd.
395  *
396  * scsi lldd's with a FC transport call this routine to temporarily stop all
397  * scsi commands to all devices managed by this scsi target.  Called 
398  * from interrupt or normal process context.
399  *
400  * Returns zero if successful or error if not
401  *
402  * Notes:       
403  *      The timeout and timer types are extracted from the fc transport 
404  *      attributes from the caller's target pointer.  This routine assumes no
405  *      locks are held on entry.
406  **/
407 int
408 fc_target_block(struct scsi_target *starget)
409 {
410         int timeout = fc_starget_dev_loss_tmo(starget);
411         struct work_struct *work = &fc_starget_dev_loss_work(starget);
412
413         if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
414                 return -EINVAL;
415
416         device_for_each_child(&starget->dev, NULL, fc_device_block);
417
418         /* The scsi lld blocks this target for the timeout period only. */
419         schedule_delayed_work(work, timeout * HZ);
420
421         return 0;
422 }
423 EXPORT_SYMBOL(fc_target_block);
424
425 /**
426  * fc_target_unblock - unblock a target following a fc_target_block request.
427  * @starget:    scsi target managed by this fc scsi lldd.       
428  *
429  * scsi lld's with a FC transport call this routine to restart IO to all 
430  * devices associated with the caller's scsi target following a fc_target_block
431  * request.  Called from interrupt or normal process context.
432  *
433  * Notes:       
434  *      This routine assumes no locks are held on entry.
435  **/
436 void
437 fc_target_unblock(struct scsi_target *starget)
438 {
439         /* 
440          * Stop the target timer first. Take no action on the del_timer
441          * failure as the state machine state change will validate the
442          * transaction. 
443          */
444         if (cancel_delayed_work(&fc_starget_dev_loss_work(starget)))
445                 flush_scheduled_work();
446
447         device_for_each_child(&starget->dev, NULL, fc_device_unblock);
448 }
449 EXPORT_SYMBOL(fc_target_unblock);
450
451 /**
452  * fc_timeout_blocked_host - Timeout handler for blocked scsi hosts
453  *                       that fail to recover in the alloted time.
454  * @data:       scsi host that failed to recover its devices in the alloted
455  *              time.
456  **/
457 static void fc_timeout_blocked_host(void  *data)
458 {
459         struct Scsi_Host *shost = (struct Scsi_Host *)data;
460         struct scsi_device *sdev;
461
462         dev_printk(KERN_ERR, &shost->shost_gendev, 
463                 "blocked host time out: host resuming\n");
464
465         shost_for_each_device(sdev, shost) {
466                 /* 
467                  * set the device going again ... if the scsi lld didn't
468                  * unblock this device, then IO errors will probably
469                  * result if the host still isn't ready.
470                  */
471                 scsi_internal_device_unblock(sdev);
472         }
473 }
474
475 /**
476  * fc_host_block - block all scsi devices managed by the calling host temporarily 
477  *              by putting each device in the SDEV_BLOCK state.
478  * @shost:      scsi host pointer that contains all scsi device siblings.
479  *
480  * scsi lld's with a FC transport call this routine to temporarily stop all
481  * scsi commands to all devices managed by this host.  Called 
482  * from interrupt or normal process context.
483  *
484  * Returns zero if successful or error if not
485  *
486  * Notes:
487  *      The timeout and timer types are extracted from the fc transport 
488  *      attributes from the caller's host pointer.  This routine assumes no
489  *      locks are held on entry.
490  **/
491 int
492 fc_host_block(struct Scsi_Host *shost)
493 {
494         struct scsi_device *sdev;
495         int timeout = fc_host_link_down_tmo(shost);
496         struct work_struct *work = &fc_host_link_down_work(shost);
497
498         if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
499                 return -EINVAL;
500
501         shost_for_each_device(sdev, shost) {
502                 scsi_internal_device_block(sdev);
503         }
504
505         schedule_delayed_work(work, timeout * HZ);
506
507         return 0;
508 }
509 EXPORT_SYMBOL(fc_host_block);
510
511 /**
512  * fc_host_unblock - unblock all devices managed by this host following a 
513  *              fc_host_block request.
514  * @shost:      scsi host containing all scsi device siblings to unblock.
515  *
516  * scsi lld's with a FC transport call this routine to restart IO to all scsi
517  * devices managed by the specified scsi host following an fc_host_block 
518  * request.  Called from interrupt or normal process context.
519  *
520  * Notes:       
521  *      This routine assumes no locks are held on entry.
522  **/
523 void
524 fc_host_unblock(struct Scsi_Host *shost)
525 {
526         struct scsi_device *sdev;
527
528         /* 
529          * Stop the host timer first. Take no action on the del_timer
530          * failure as the state machine state change will validate the
531          * transaction.
532          */
533         if (cancel_delayed_work(&fc_host_link_down_work(shost)))
534                 flush_scheduled_work();
535
536         shost_for_each_device(sdev, shost) {
537                 scsi_internal_device_unblock(sdev);
538         }
539 }
540 EXPORT_SYMBOL(fc_host_unblock);
541
542 MODULE_AUTHOR("Martin Hicks");
543 MODULE_DESCRIPTION("FC Transport Attributes");
544 MODULE_LICENSE("GPL");
545
546 module_init(fc_transport_init);
547 module_exit(fc_transport_exit);