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