diff options
author | Boaz Harrosh <bharrosh@panasas.com> | 2009-01-25 09:59:50 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-12 13:58:07 -0400 |
commit | 4ef1a3d70d02663f6bfe901db629e8e608da15b1 (patch) | |
tree | f3b7f1f6ad65fcbc81af1dbdd0b0080d315e4df2 /drivers/scsi | |
parent | b799bc7da0ce5ba4a988c521a8fb10452eb419f0 (diff) |
[SCSI] libosd: attributes Support
Support for both List-Mode and Page-Mode osd attributes. One of
these operations may be added to most other operations.
Define the OSD standard's attribute pages constants and structures
(osd_attributes.h)
Signed-off-by: Boaz Harrosh <bharrosh@panasas.com>
Reviewed-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/osd/osd_initiator.c | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 0e6d906f1113..b982d767bf99 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c | |||
@@ -45,6 +45,10 @@ | |||
45 | 45 | ||
46 | #include "osd_debug.h" | 46 | #include "osd_debug.h" |
47 | 47 | ||
48 | #ifndef __unused | ||
49 | # define __unused __attribute__((unused)) | ||
50 | #endif | ||
51 | |||
48 | enum { OSD_REQ_RETRIES = 1 }; | 52 | enum { OSD_REQ_RETRIES = 1 }; |
49 | 53 | ||
50 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); | 54 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); |
@@ -63,6 +67,50 @@ static unsigned _osd_req_cdb_len(struct osd_request *or) | |||
63 | return OSDv1_TOTAL_CDB_LEN; | 67 | return OSDv1_TOTAL_CDB_LEN; |
64 | } | 68 | } |
65 | 69 | ||
70 | static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len) | ||
71 | { | ||
72 | return osdv1_attr_list_elem_size(len); | ||
73 | } | ||
74 | |||
75 | static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head) | ||
76 | { | ||
77 | return osdv1_list_size(list_head); | ||
78 | } | ||
79 | |||
80 | static unsigned _osd_req_sizeof_alist_header(struct osd_request *or) | ||
81 | { | ||
82 | return sizeof(struct osdv1_attributes_list_header); | ||
83 | } | ||
84 | |||
85 | static void _osd_req_set_alist_type(struct osd_request *or, | ||
86 | void *list, int list_type) | ||
87 | { | ||
88 | struct osdv1_attributes_list_header *attr_list = list; | ||
89 | |||
90 | memset(attr_list, 0, sizeof(*attr_list)); | ||
91 | attr_list->type = list_type; | ||
92 | } | ||
93 | |||
94 | static bool _osd_req_is_alist_type(struct osd_request *or, | ||
95 | void *list, int list_type) | ||
96 | { | ||
97 | if (!list) | ||
98 | return false; | ||
99 | |||
100 | if (1) { | ||
101 | struct osdv1_attributes_list_header *attr_list = list; | ||
102 | |||
103 | return attr_list->type == list_type; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static osd_cdb_offset osd_req_encode_offset(struct osd_request *or, | ||
108 | u64 offset, unsigned *padding) | ||
109 | { | ||
110 | return __osd_encode_offset(offset, padding, | ||
111 | OSDv1_OFFSET_MIN_SHIFT, OSD_OFFSET_MAX_SHIFT); | ||
112 | } | ||
113 | |||
66 | void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device) | 114 | void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device) |
67 | { | 115 | { |
68 | memset(osdd, 0, sizeof(*osdd)); | 116 | memset(osdd, 0, sizeof(*osdd)); |
@@ -125,10 +173,25 @@ static void _abort_unexecuted_bios(struct request *rq) | |||
125 | } | 173 | } |
126 | } | 174 | } |
127 | 175 | ||
176 | static void _osd_free_seg(struct osd_request *or __unused, | ||
177 | struct _osd_req_data_segment *seg) | ||
178 | { | ||
179 | if (!seg->buff || !seg->alloc_size) | ||
180 | return; | ||
181 | |||
182 | kfree(seg->buff); | ||
183 | seg->buff = NULL; | ||
184 | seg->alloc_size = 0; | ||
185 | } | ||
186 | |||
128 | void osd_end_request(struct osd_request *or) | 187 | void osd_end_request(struct osd_request *or) |
129 | { | 188 | { |
130 | struct request *rq = or->request; | 189 | struct request *rq = or->request; |
131 | 190 | ||
191 | _osd_free_seg(or, &or->set_attr); | ||
192 | _osd_free_seg(or, &or->enc_get_attr); | ||
193 | _osd_free_seg(or, &or->get_attr); | ||
194 | |||
132 | if (rq) { | 195 | if (rq) { |
133 | if (rq->next_rq) { | 196 | if (rq->next_rq) { |
134 | _abort_unexecuted_bios(rq->next_rq); | 197 | _abort_unexecuted_bios(rq->next_rq); |
@@ -176,6 +239,54 @@ int osd_execute_request_async(struct osd_request *or, | |||
176 | } | 239 | } |
177 | EXPORT_SYMBOL(osd_execute_request_async); | 240 | EXPORT_SYMBOL(osd_execute_request_async); |
178 | 241 | ||
242 | u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; | ||
243 | u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT]; | ||
244 | |||
245 | static int _osd_realloc_seg(struct osd_request *or, | ||
246 | struct _osd_req_data_segment *seg, unsigned max_bytes) | ||
247 | { | ||
248 | void *buff; | ||
249 | |||
250 | if (seg->alloc_size >= max_bytes) | ||
251 | return 0; | ||
252 | |||
253 | buff = krealloc(seg->buff, max_bytes, or->alloc_flags); | ||
254 | if (!buff) { | ||
255 | OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes, | ||
256 | seg->alloc_size); | ||
257 | return -ENOMEM; | ||
258 | } | ||
259 | |||
260 | memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size); | ||
261 | seg->buff = buff; | ||
262 | seg->alloc_size = max_bytes; | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int _alloc_set_attr_list(struct osd_request *or, | ||
267 | const struct osd_attr *oa, unsigned nelem, unsigned add_bytes) | ||
268 | { | ||
269 | unsigned total_bytes = add_bytes; | ||
270 | |||
271 | for (; nelem; --nelem, ++oa) | ||
272 | total_bytes += _osd_req_alist_elem_size(or, oa->len); | ||
273 | |||
274 | OSD_DEBUG("total_bytes=%d\n", total_bytes); | ||
275 | return _osd_realloc_seg(or, &or->set_attr, total_bytes); | ||
276 | } | ||
277 | |||
278 | static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes) | ||
279 | { | ||
280 | OSD_DEBUG("total_bytes=%d\n", max_bytes); | ||
281 | return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes); | ||
282 | } | ||
283 | |||
284 | static int _alloc_get_attr_list(struct osd_request *or) | ||
285 | { | ||
286 | OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes); | ||
287 | return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes); | ||
288 | } | ||
289 | |||
179 | /* | 290 | /* |
180 | * Common to all OSD commands | 291 | * Common to all OSD commands |
181 | */ | 292 | */ |
@@ -284,6 +395,410 @@ void osd_req_read(struct osd_request *or, | |||
284 | } | 395 | } |
285 | EXPORT_SYMBOL(osd_req_read); | 396 | EXPORT_SYMBOL(osd_req_read); |
286 | 397 | ||
398 | void osd_req_get_attributes(struct osd_request *or, | ||
399 | const struct osd_obj_id *obj) | ||
400 | { | ||
401 | _osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0); | ||
402 | } | ||
403 | EXPORT_SYMBOL(osd_req_get_attributes); | ||
404 | |||
405 | void osd_req_set_attributes(struct osd_request *or, | ||
406 | const struct osd_obj_id *obj) | ||
407 | { | ||
408 | _osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0); | ||
409 | } | ||
410 | EXPORT_SYMBOL(osd_req_set_attributes); | ||
411 | |||
412 | /* | ||
413 | * Attributes List-mode | ||
414 | */ | ||
415 | |||
416 | int osd_req_add_set_attr_list(struct osd_request *or, | ||
417 | const struct osd_attr *oa, unsigned nelem) | ||
418 | { | ||
419 | unsigned total_bytes = or->set_attr.total_bytes; | ||
420 | void *attr_last; | ||
421 | int ret; | ||
422 | |||
423 | if (or->attributes_mode && | ||
424 | or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { | ||
425 | WARN_ON(1); | ||
426 | return -EINVAL; | ||
427 | } | ||
428 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | ||
429 | |||
430 | if (!total_bytes) { /* first-time: allocate and put list header */ | ||
431 | total_bytes = _osd_req_sizeof_alist_header(or); | ||
432 | ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); | ||
433 | if (ret) | ||
434 | return ret; | ||
435 | _osd_req_set_alist_type(or, or->set_attr.buff, | ||
436 | OSD_ATTR_LIST_SET_RETRIEVE); | ||
437 | } | ||
438 | attr_last = or->set_attr.buff + total_bytes; | ||
439 | |||
440 | for (; nelem; --nelem) { | ||
441 | struct osd_attributes_list_element *attr; | ||
442 | unsigned elem_size = _osd_req_alist_elem_size(or, oa->len); | ||
443 | |||
444 | total_bytes += elem_size; | ||
445 | if (unlikely(or->set_attr.alloc_size < total_bytes)) { | ||
446 | or->set_attr.total_bytes = total_bytes - elem_size; | ||
447 | ret = _alloc_set_attr_list(or, oa, nelem, total_bytes); | ||
448 | if (ret) | ||
449 | return ret; | ||
450 | attr_last = | ||
451 | or->set_attr.buff + or->set_attr.total_bytes; | ||
452 | } | ||
453 | |||
454 | attr = attr_last; | ||
455 | attr->attr_page = cpu_to_be32(oa->attr_page); | ||
456 | attr->attr_id = cpu_to_be32(oa->attr_id); | ||
457 | attr->attr_bytes = cpu_to_be16(oa->len); | ||
458 | memcpy(attr->attr_val, oa->val_ptr, oa->len); | ||
459 | |||
460 | attr_last += elem_size; | ||
461 | ++oa; | ||
462 | } | ||
463 | |||
464 | or->set_attr.total_bytes = total_bytes; | ||
465 | return 0; | ||
466 | } | ||
467 | EXPORT_SYMBOL(osd_req_add_set_attr_list); | ||
468 | |||
469 | static int _append_map_kern(struct request *req, | ||
470 | void *buff, unsigned len, gfp_t flags) | ||
471 | { | ||
472 | struct bio *bio; | ||
473 | int ret; | ||
474 | |||
475 | bio = bio_map_kern(req->q, buff, len, flags); | ||
476 | if (IS_ERR(bio)) { | ||
477 | OSD_ERR("Failed bio_map_kern(%p, %d) => %ld\n", buff, len, | ||
478 | PTR_ERR(bio)); | ||
479 | return PTR_ERR(bio); | ||
480 | } | ||
481 | ret = blk_rq_append_bio(req->q, req, bio); | ||
482 | if (ret) { | ||
483 | OSD_ERR("Failed blk_rq_append_bio(%p) => %d\n", bio, ret); | ||
484 | bio_put(bio); | ||
485 | } | ||
486 | return ret; | ||
487 | } | ||
488 | |||
489 | static int _req_append_segment(struct osd_request *or, | ||
490 | unsigned padding, struct _osd_req_data_segment *seg, | ||
491 | struct _osd_req_data_segment *last_seg, struct _osd_io_info *io) | ||
492 | { | ||
493 | void *pad_buff; | ||
494 | int ret; | ||
495 | |||
496 | if (padding) { | ||
497 | /* check if we can just add it to last buffer */ | ||
498 | if (last_seg && | ||
499 | (padding <= last_seg->alloc_size - last_seg->total_bytes)) | ||
500 | pad_buff = last_seg->buff + last_seg->total_bytes; | ||
501 | else | ||
502 | pad_buff = io->pad_buff; | ||
503 | |||
504 | ret = _append_map_kern(io->req, pad_buff, padding, | ||
505 | or->alloc_flags); | ||
506 | if (ret) | ||
507 | return ret; | ||
508 | io->total_bytes += padding; | ||
509 | } | ||
510 | |||
511 | ret = _append_map_kern(io->req, seg->buff, seg->total_bytes, | ||
512 | or->alloc_flags); | ||
513 | if (ret) | ||
514 | return ret; | ||
515 | |||
516 | io->total_bytes += seg->total_bytes; | ||
517 | OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff, | ||
518 | seg->total_bytes); | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | static int _osd_req_finalize_set_attr_list(struct osd_request *or) | ||
523 | { | ||
524 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
525 | unsigned padding; | ||
526 | int ret; | ||
527 | |||
528 | if (!or->set_attr.total_bytes) { | ||
529 | cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED; | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes); | ||
534 | cdbh->attrs_list.set_attr_offset = | ||
535 | osd_req_encode_offset(or, or->out.total_bytes, &padding); | ||
536 | |||
537 | ret = _req_append_segment(or, padding, &or->set_attr, | ||
538 | or->out.last_seg, &or->out); | ||
539 | if (ret) | ||
540 | return ret; | ||
541 | |||
542 | or->out.last_seg = &or->set_attr; | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | int osd_req_add_get_attr_list(struct osd_request *or, | ||
547 | const struct osd_attr *oa, unsigned nelem) | ||
548 | { | ||
549 | unsigned total_bytes = or->enc_get_attr.total_bytes; | ||
550 | void *attr_last; | ||
551 | int ret; | ||
552 | |||
553 | if (or->attributes_mode && | ||
554 | or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) { | ||
555 | WARN_ON(1); | ||
556 | return -EINVAL; | ||
557 | } | ||
558 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | ||
559 | |||
560 | /* first time calc data-in list header size */ | ||
561 | if (!or->get_attr.total_bytes) | ||
562 | or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or); | ||
563 | |||
564 | /* calc data-out info */ | ||
565 | if (!total_bytes) { /* first-time: allocate and put list header */ | ||
566 | unsigned max_bytes; | ||
567 | |||
568 | total_bytes = _osd_req_sizeof_alist_header(or); | ||
569 | max_bytes = total_bytes + | ||
570 | nelem * sizeof(struct osd_attributes_list_attrid); | ||
571 | ret = _alloc_get_attr_desc(or, max_bytes); | ||
572 | if (ret) | ||
573 | return ret; | ||
574 | |||
575 | _osd_req_set_alist_type(or, or->enc_get_attr.buff, | ||
576 | OSD_ATTR_LIST_GET); | ||
577 | } | ||
578 | attr_last = or->enc_get_attr.buff + total_bytes; | ||
579 | |||
580 | for (; nelem; --nelem) { | ||
581 | struct osd_attributes_list_attrid *attrid; | ||
582 | const unsigned cur_size = sizeof(*attrid); | ||
583 | |||
584 | total_bytes += cur_size; | ||
585 | if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) { | ||
586 | or->enc_get_attr.total_bytes = total_bytes - cur_size; | ||
587 | ret = _alloc_get_attr_desc(or, | ||
588 | total_bytes + nelem * sizeof(*attrid)); | ||
589 | if (ret) | ||
590 | return ret; | ||
591 | attr_last = or->enc_get_attr.buff + | ||
592 | or->enc_get_attr.total_bytes; | ||
593 | } | ||
594 | |||
595 | attrid = attr_last; | ||
596 | attrid->attr_page = cpu_to_be32(oa->attr_page); | ||
597 | attrid->attr_id = cpu_to_be32(oa->attr_id); | ||
598 | |||
599 | attr_last += cur_size; | ||
600 | |||
601 | /* calc data-in size */ | ||
602 | or->get_attr.total_bytes += | ||
603 | _osd_req_alist_elem_size(or, oa->len); | ||
604 | ++oa; | ||
605 | } | ||
606 | |||
607 | or->enc_get_attr.total_bytes = total_bytes; | ||
608 | |||
609 | OSD_DEBUG( | ||
610 | "get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%Zu)\n", | ||
611 | or->get_attr.total_bytes, | ||
612 | or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or), | ||
613 | or->enc_get_attr.total_bytes, | ||
614 | (or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or)) | ||
615 | / sizeof(struct osd_attributes_list_attrid)); | ||
616 | |||
617 | return 0; | ||
618 | } | ||
619 | EXPORT_SYMBOL(osd_req_add_get_attr_list); | ||
620 | |||
621 | static int _osd_req_finalize_get_attr_list(struct osd_request *or) | ||
622 | { | ||
623 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
624 | unsigned out_padding; | ||
625 | unsigned in_padding; | ||
626 | int ret; | ||
627 | |||
628 | if (!or->enc_get_attr.total_bytes) { | ||
629 | cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED; | ||
630 | cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | ret = _alloc_get_attr_list(or); | ||
635 | if (ret) | ||
636 | return ret; | ||
637 | |||
638 | /* The out-going buffer info update */ | ||
639 | OSD_DEBUG("out-going\n"); | ||
640 | cdbh->attrs_list.get_attr_desc_bytes = | ||
641 | cpu_to_be32(or->enc_get_attr.total_bytes); | ||
642 | |||
643 | cdbh->attrs_list.get_attr_desc_offset = | ||
644 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); | ||
645 | |||
646 | ret = _req_append_segment(or, out_padding, &or->enc_get_attr, | ||
647 | or->out.last_seg, &or->out); | ||
648 | if (ret) | ||
649 | return ret; | ||
650 | or->out.last_seg = &or->enc_get_attr; | ||
651 | |||
652 | /* The incoming buffer info update */ | ||
653 | OSD_DEBUG("in-coming\n"); | ||
654 | cdbh->attrs_list.get_attr_alloc_length = | ||
655 | cpu_to_be32(or->get_attr.total_bytes); | ||
656 | |||
657 | cdbh->attrs_list.get_attr_offset = | ||
658 | osd_req_encode_offset(or, or->in.total_bytes, &in_padding); | ||
659 | |||
660 | ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, | ||
661 | &or->in); | ||
662 | if (ret) | ||
663 | return ret; | ||
664 | or->in.last_seg = &or->get_attr; | ||
665 | |||
666 | return 0; | ||
667 | } | ||
668 | |||
669 | int osd_req_decode_get_attr_list(struct osd_request *or, | ||
670 | struct osd_attr *oa, int *nelem, void **iterator) | ||
671 | { | ||
672 | unsigned cur_bytes, returned_bytes; | ||
673 | int n; | ||
674 | const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or); | ||
675 | void *cur_p; | ||
676 | |||
677 | if (!_osd_req_is_alist_type(or, or->get_attr.buff, | ||
678 | OSD_ATTR_LIST_SET_RETRIEVE)) { | ||
679 | oa->attr_page = 0; | ||
680 | oa->attr_id = 0; | ||
681 | oa->val_ptr = NULL; | ||
682 | oa->len = 0; | ||
683 | *iterator = NULL; | ||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | if (*iterator) { | ||
688 | BUG_ON((*iterator < or->get_attr.buff) || | ||
689 | (or->get_attr.buff + or->get_attr.alloc_size < *iterator)); | ||
690 | cur_p = *iterator; | ||
691 | cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list; | ||
692 | returned_bytes = or->get_attr.total_bytes; | ||
693 | } else { /* first time decode the list header */ | ||
694 | cur_bytes = sizeof_attr_list; | ||
695 | returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) + | ||
696 | sizeof_attr_list; | ||
697 | |||
698 | cur_p = or->get_attr.buff + sizeof_attr_list; | ||
699 | |||
700 | if (returned_bytes > or->get_attr.alloc_size) { | ||
701 | OSD_DEBUG("target report: space was not big enough! " | ||
702 | "Allocate=%u Needed=%u\n", | ||
703 | or->get_attr.alloc_size, | ||
704 | returned_bytes + sizeof_attr_list); | ||
705 | |||
706 | returned_bytes = | ||
707 | or->get_attr.alloc_size - sizeof_attr_list; | ||
708 | } | ||
709 | or->get_attr.total_bytes = returned_bytes; | ||
710 | } | ||
711 | |||
712 | for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) { | ||
713 | struct osd_attributes_list_element *attr = cur_p; | ||
714 | unsigned inc; | ||
715 | |||
716 | oa->len = be16_to_cpu(attr->attr_bytes); | ||
717 | inc = _osd_req_alist_elem_size(or, oa->len); | ||
718 | OSD_DEBUG("oa->len=%d inc=%d cur_bytes=%d\n", | ||
719 | oa->len, inc, cur_bytes); | ||
720 | cur_bytes += inc; | ||
721 | if (cur_bytes > returned_bytes) { | ||
722 | OSD_ERR("BAD FOOD from target. list not valid!" | ||
723 | "c=%d r=%d n=%d\n", | ||
724 | cur_bytes, returned_bytes, n); | ||
725 | oa->val_ptr = NULL; | ||
726 | break; | ||
727 | } | ||
728 | |||
729 | oa->attr_page = be32_to_cpu(attr->attr_page); | ||
730 | oa->attr_id = be32_to_cpu(attr->attr_id); | ||
731 | oa->val_ptr = attr->attr_val; | ||
732 | |||
733 | cur_p += inc; | ||
734 | ++oa; | ||
735 | } | ||
736 | |||
737 | *iterator = (returned_bytes - cur_bytes) ? cur_p : NULL; | ||
738 | *nelem = n; | ||
739 | return returned_bytes - cur_bytes; | ||
740 | } | ||
741 | EXPORT_SYMBOL(osd_req_decode_get_attr_list); | ||
742 | |||
743 | /* | ||
744 | * Attributes Page-mode | ||
745 | */ | ||
746 | |||
747 | int osd_req_add_get_attr_page(struct osd_request *or, | ||
748 | u32 page_id, void *attar_page, unsigned max_page_len, | ||
749 | const struct osd_attr *set_one_attr) | ||
750 | { | ||
751 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
752 | |||
753 | if (or->attributes_mode && | ||
754 | or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) { | ||
755 | WARN_ON(1); | ||
756 | return -EINVAL; | ||
757 | } | ||
758 | or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE; | ||
759 | |||
760 | or->get_attr.buff = attar_page; | ||
761 | or->get_attr.total_bytes = max_page_len; | ||
762 | |||
763 | or->set_attr.buff = set_one_attr->val_ptr; | ||
764 | or->set_attr.total_bytes = set_one_attr->len; | ||
765 | |||
766 | cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id); | ||
767 | cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len); | ||
768 | /* ocdb->attrs_page.get_attr_offset; */ | ||
769 | |||
770 | cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page); | ||
771 | cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id); | ||
772 | cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len); | ||
773 | /* ocdb->attrs_page.set_attr_offset; */ | ||
774 | return 0; | ||
775 | } | ||
776 | EXPORT_SYMBOL(osd_req_add_get_attr_page); | ||
777 | |||
778 | static int _osd_req_finalize_attr_page(struct osd_request *or) | ||
779 | { | ||
780 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); | ||
781 | unsigned in_padding, out_padding; | ||
782 | int ret; | ||
783 | |||
784 | /* returned page */ | ||
785 | cdbh->attrs_page.get_attr_offset = | ||
786 | osd_req_encode_offset(or, or->in.total_bytes, &in_padding); | ||
787 | |||
788 | ret = _req_append_segment(or, in_padding, &or->get_attr, NULL, | ||
789 | &or->in); | ||
790 | if (ret) | ||
791 | return ret; | ||
792 | |||
793 | /* set one value */ | ||
794 | cdbh->attrs_page.set_attr_offset = | ||
795 | osd_req_encode_offset(or, or->out.total_bytes, &out_padding); | ||
796 | |||
797 | ret = _req_append_segment(or, out_padding, &or->enc_get_attr, NULL, | ||
798 | &or->out); | ||
799 | return ret; | ||
800 | } | ||
801 | |||
287 | /* | 802 | /* |
288 | * osd_finalize_request and helpers | 803 | * osd_finalize_request and helpers |
289 | */ | 804 | */ |
@@ -378,9 +893,31 @@ int osd_finalize_request(struct osd_request *or, | |||
378 | _LLU(or->in.total_bytes), or->in.req->data_len); | 893 | _LLU(or->in.total_bytes), or->in.req->data_len); |
379 | } | 894 | } |
380 | 895 | ||
896 | or->out.pad_buff = sg_out_pad_buffer; | ||
897 | or->in.pad_buff = sg_in_pad_buffer; | ||
898 | |||
381 | if (!or->attributes_mode) | 899 | if (!or->attributes_mode) |
382 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; | 900 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; |
383 | cdbh->command_specific_options |= or->attributes_mode; | 901 | cdbh->command_specific_options |= or->attributes_mode; |
902 | if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) { | ||
903 | ret = _osd_req_finalize_attr_page(or); | ||
904 | } else { | ||
905 | /* TODO: I think that for the GET_ATTR command these 2 should | ||
906 | * be reversed to keep them in execution order (for embeded | ||
907 | * targets with low memory footprint) | ||
908 | */ | ||
909 | ret = _osd_req_finalize_set_attr_list(or); | ||
910 | if (ret) { | ||
911 | OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n"); | ||
912 | return ret; | ||
913 | } | ||
914 | |||
915 | ret = _osd_req_finalize_get_attr_list(or); | ||
916 | if (ret) { | ||
917 | OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n"); | ||
918 | return ret; | ||
919 | } | ||
920 | } | ||
384 | 921 | ||
385 | or->request->cmd = or->cdb.buff; | 922 | or->request->cmd = or->cdb.buff; |
386 | or->request->cmd_len = _osd_req_cdb_len(or); | 923 | or->request->cmd_len = _osd_req_cdb_len(or); |
@@ -446,3 +983,45 @@ void osd_set_caps(struct osd_cdb *cdb, const void *caps) | |||
446 | { | 983 | { |
447 | memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN); | 984 | memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN); |
448 | } | 985 | } |
986 | |||
987 | /* | ||
988 | * Declared in osd_protocol.h | ||
989 | * 4.12.5 Data-In and Data-Out buffer offsets | ||
990 | * byte offset = mantissa * (2^(exponent+8)) | ||
991 | * Returns the smallest allowed encoded offset that contains given @offset | ||
992 | * The actual encoded offset returned is @offset + *@padding. | ||
993 | */ | ||
994 | osd_cdb_offset __osd_encode_offset( | ||
995 | u64 offset, unsigned *padding, int min_shift, int max_shift) | ||
996 | { | ||
997 | u64 try_offset = -1, mod, align; | ||
998 | osd_cdb_offset be32_offset; | ||
999 | int shift; | ||
1000 | |||
1001 | *padding = 0; | ||
1002 | if (!offset) | ||
1003 | return 0; | ||
1004 | |||
1005 | for (shift = min_shift; shift < max_shift; ++shift) { | ||
1006 | try_offset = offset >> shift; | ||
1007 | if (try_offset < (1 << OSD_OFFSET_MAX_BITS)) | ||
1008 | break; | ||
1009 | } | ||
1010 | |||
1011 | BUG_ON(shift == max_shift); | ||
1012 | |||
1013 | align = 1 << shift; | ||
1014 | mod = offset & (align - 1); | ||
1015 | if (mod) { | ||
1016 | *padding = align - mod; | ||
1017 | try_offset += 1; | ||
1018 | } | ||
1019 | |||
1020 | try_offset |= ((shift - 8) & 0xf) << 28; | ||
1021 | be32_offset = cpu_to_be32((u32)try_offset); | ||
1022 | |||
1023 | OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n", | ||
1024 | _LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift, | ||
1025 | be32_offset, *padding); | ||
1026 | return be32_offset; | ||
1027 | } | ||