Boaz Harrosh | 02941a5 | 2009-01-25 16:55:30 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * osd_initiator - Main body of the osd initiator library. |
| 3 | * |
| 4 | * Note: The file does not contain the advanced security functionality which |
| 5 | * is only needed by the security_manager's initiators. |
| 6 | * |
| 7 | * Copyright (C) 2008 Panasas Inc. All rights reserved. |
| 8 | * |
| 9 | * Authors: |
| 10 | * Boaz Harrosh <bharrosh@panasas.com> |
| 11 | * Benny Halevy <bhalevy@panasas.com> |
| 12 | * |
| 13 | * This program is free software; you can redistribute it and/or modify |
| 14 | * it under the terms of the GNU General Public License version 2 |
| 15 | * |
| 16 | * Redistribution and use in source and binary forms, with or without |
| 17 | * modification, are permitted provided that the following conditions |
| 18 | * are met: |
| 19 | * |
| 20 | * 1. Redistributions of source code must retain the above copyright |
| 21 | * notice, this list of conditions and the following disclaimer. |
| 22 | * 2. Redistributions in binary form must reproduce the above copyright |
| 23 | * notice, this list of conditions and the following disclaimer in the |
| 24 | * documentation and/or other materials provided with the distribution. |
| 25 | * 3. Neither the name of the Panasas company nor the names of its |
| 26 | * contributors may be used to endorse or promote products derived |
| 27 | * from this software without specific prior written permission. |
| 28 | * |
| 29 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 30 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 31 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 32 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 33 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 34 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 35 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| 36 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| 37 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 38 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 39 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 40 | */ |
| 41 | |
| 42 | #include <scsi/osd_initiator.h> |
| 43 | #include <scsi/osd_sec.h> |
| 44 | #include <scsi/scsi_device.h> |
| 45 | |
| 46 | #include "osd_debug.h" |
| 47 | |
| 48 | enum { OSD_REQ_RETRIES = 1 }; |
| 49 | |
| 50 | MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>"); |
| 51 | MODULE_DESCRIPTION("open-osd initiator library libosd.ko"); |
| 52 | MODULE_LICENSE("GPL"); |
| 53 | |
| 54 | static inline void build_test(void) |
| 55 | { |
| 56 | /* structures were not packed */ |
| 57 | BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN); |
| 58 | BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN); |
| 59 | } |
| 60 | |
| 61 | static unsigned _osd_req_cdb_len(struct osd_request *or) |
| 62 | { |
| 63 | return OSDv1_TOTAL_CDB_LEN; |
| 64 | } |
| 65 | |
| 66 | void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device) |
| 67 | { |
| 68 | memset(osdd, 0, sizeof(*osdd)); |
| 69 | osdd->scsi_device = scsi_device; |
| 70 | osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT; |
| 71 | /* TODO: Allocate pools for osd_request attributes ... */ |
| 72 | } |
| 73 | EXPORT_SYMBOL(osd_dev_init); |
| 74 | |
| 75 | void osd_dev_fini(struct osd_dev *osdd) |
| 76 | { |
| 77 | /* TODO: De-allocate pools */ |
| 78 | |
| 79 | osdd->scsi_device = NULL; |
| 80 | } |
| 81 | EXPORT_SYMBOL(osd_dev_fini); |
| 82 | |
| 83 | static struct osd_request *_osd_request_alloc(gfp_t gfp) |
| 84 | { |
| 85 | struct osd_request *or; |
| 86 | |
| 87 | /* TODO: Use mempool with one saved request */ |
| 88 | or = kzalloc(sizeof(*or), gfp); |
| 89 | return or; |
| 90 | } |
| 91 | |
| 92 | static void _osd_request_free(struct osd_request *or) |
| 93 | { |
| 94 | kfree(or); |
| 95 | } |
| 96 | |
| 97 | struct osd_request *osd_start_request(struct osd_dev *dev, gfp_t gfp) |
| 98 | { |
| 99 | struct osd_request *or; |
| 100 | |
| 101 | or = _osd_request_alloc(gfp); |
| 102 | if (!or) |
| 103 | return NULL; |
| 104 | |
| 105 | or->osd_dev = dev; |
| 106 | or->alloc_flags = gfp; |
| 107 | or->timeout = dev->def_timeout; |
| 108 | or->retries = OSD_REQ_RETRIES; |
| 109 | |
| 110 | return or; |
| 111 | } |
| 112 | EXPORT_SYMBOL(osd_start_request); |
| 113 | |
| 114 | /* |
| 115 | * If osd_finalize_request() was called but the request was not executed through |
| 116 | * the block layer, then we must release BIOs. |
| 117 | */ |
| 118 | static void _abort_unexecuted_bios(struct request *rq) |
| 119 | { |
| 120 | struct bio *bio; |
| 121 | |
| 122 | while ((bio = rq->bio) != NULL) { |
| 123 | rq->bio = bio->bi_next; |
| 124 | bio_endio(bio, 0); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | void osd_end_request(struct osd_request *or) |
| 129 | { |
| 130 | struct request *rq = or->request; |
| 131 | |
| 132 | if (rq) { |
| 133 | if (rq->next_rq) { |
| 134 | _abort_unexecuted_bios(rq->next_rq); |
| 135 | blk_put_request(rq->next_rq); |
| 136 | } |
| 137 | |
| 138 | _abort_unexecuted_bios(rq); |
| 139 | blk_put_request(rq); |
| 140 | } |
| 141 | _osd_request_free(or); |
| 142 | } |
| 143 | EXPORT_SYMBOL(osd_end_request); |
| 144 | |
| 145 | int osd_execute_request(struct osd_request *or) |
| 146 | { |
| 147 | return blk_execute_rq(or->request->q, NULL, or->request, 0); |
| 148 | } |
| 149 | EXPORT_SYMBOL(osd_execute_request); |
| 150 | |
| 151 | static void osd_request_async_done(struct request *req, int error) |
| 152 | { |
| 153 | struct osd_request *or = req->end_io_data; |
| 154 | |
| 155 | or->async_error = error; |
| 156 | |
| 157 | if (error) |
| 158 | OSD_DEBUG("osd_request_async_done error recieved %d\n", error); |
| 159 | |
| 160 | if (or->async_done) |
| 161 | or->async_done(or, or->async_private); |
| 162 | else |
| 163 | osd_end_request(or); |
| 164 | } |
| 165 | |
| 166 | int osd_execute_request_async(struct osd_request *or, |
| 167 | osd_req_done_fn *done, void *private) |
| 168 | { |
| 169 | or->request->end_io_data = or; |
| 170 | or->async_private = private; |
| 171 | or->async_done = done; |
| 172 | |
| 173 | blk_execute_rq_nowait(or->request->q, NULL, or->request, 0, |
| 174 | osd_request_async_done); |
| 175 | return 0; |
| 176 | } |
| 177 | EXPORT_SYMBOL(osd_execute_request_async); |
| 178 | |
| 179 | /* |
| 180 | * Common to all OSD commands |
| 181 | */ |
| 182 | |
| 183 | static void _osdv1_req_encode_common(struct osd_request *or, |
| 184 | __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) |
| 185 | { |
| 186 | struct osdv1_cdb *ocdb = &or->cdb.v1; |
| 187 | |
| 188 | /* |
| 189 | * For speed, the commands |
| 190 | * OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C |
| 191 | * OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D |
| 192 | * are not supported here. Should pass zero and set after the call |
| 193 | */ |
| 194 | act &= cpu_to_be16(~0x0080); /* V1 action code */ |
| 195 | |
| 196 | OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act)); |
| 197 | |
| 198 | ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD; |
| 199 | ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH; |
| 200 | ocdb->h.varlen_cdb.service_action = act; |
| 201 | |
| 202 | ocdb->h.partition = cpu_to_be64(obj->partition); |
| 203 | ocdb->h.object = cpu_to_be64(obj->id); |
| 204 | ocdb->h.v1.length = cpu_to_be64(len); |
| 205 | ocdb->h.v1.start_address = cpu_to_be64(offset); |
| 206 | } |
| 207 | |
| 208 | static void _osd_req_encode_common(struct osd_request *or, |
| 209 | __be16 act, const struct osd_obj_id *obj, u64 offset, u64 len) |
| 210 | { |
| 211 | _osdv1_req_encode_common(or, act, obj, offset, len); |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | * Device commands |
| 216 | */ |
| 217 | void osd_req_format(struct osd_request *or, u64 tot_capacity) |
| 218 | { |
| 219 | _osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0, |
| 220 | tot_capacity); |
| 221 | } |
| 222 | EXPORT_SYMBOL(osd_req_format); |
| 223 | |
| 224 | /* |
| 225 | * Partition commands |
| 226 | */ |
| 227 | static void _osd_req_encode_partition(struct osd_request *or, |
| 228 | __be16 act, osd_id partition) |
| 229 | { |
| 230 | struct osd_obj_id par = { |
| 231 | .partition = partition, |
| 232 | .id = 0, |
| 233 | }; |
| 234 | |
| 235 | _osd_req_encode_common(or, act, &par, 0, 0); |
| 236 | } |
| 237 | |
| 238 | void osd_req_create_partition(struct osd_request *or, osd_id partition) |
| 239 | { |
| 240 | _osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition); |
| 241 | } |
| 242 | EXPORT_SYMBOL(osd_req_create_partition); |
| 243 | |
| 244 | void osd_req_remove_partition(struct osd_request *or, osd_id partition) |
| 245 | { |
| 246 | _osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition); |
| 247 | } |
| 248 | EXPORT_SYMBOL(osd_req_remove_partition); |
| 249 | |
| 250 | /* |
| 251 | * Object commands |
| 252 | */ |
| 253 | void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj) |
| 254 | { |
| 255 | _osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0); |
| 256 | } |
| 257 | EXPORT_SYMBOL(osd_req_create_object); |
| 258 | |
| 259 | void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj) |
| 260 | { |
| 261 | _osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0); |
| 262 | } |
| 263 | EXPORT_SYMBOL(osd_req_remove_object); |
| 264 | |
| 265 | void osd_req_write(struct osd_request *or, |
| 266 | const struct osd_obj_id *obj, struct bio *bio, u64 offset) |
| 267 | { |
| 268 | _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, bio->bi_size); |
| 269 | WARN_ON(or->out.bio || or->out.total_bytes); |
| 270 | bio->bi_rw |= (1 << BIO_RW); |
| 271 | or->out.bio = bio; |
| 272 | or->out.total_bytes = bio->bi_size; |
| 273 | } |
| 274 | EXPORT_SYMBOL(osd_req_write); |
| 275 | |
| 276 | void osd_req_read(struct osd_request *or, |
| 277 | const struct osd_obj_id *obj, struct bio *bio, u64 offset) |
| 278 | { |
| 279 | _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, bio->bi_size); |
| 280 | WARN_ON(or->in.bio || or->in.total_bytes); |
| 281 | bio->bi_rw &= ~(1 << BIO_RW); |
| 282 | or->in.bio = bio; |
| 283 | or->in.total_bytes = bio->bi_size; |
| 284 | } |
| 285 | EXPORT_SYMBOL(osd_req_read); |
| 286 | |
| 287 | /* |
| 288 | * osd_finalize_request and helpers |
| 289 | */ |
| 290 | |
| 291 | static int _init_blk_request(struct osd_request *or, |
| 292 | bool has_in, bool has_out) |
| 293 | { |
| 294 | gfp_t flags = or->alloc_flags; |
| 295 | struct scsi_device *scsi_device = or->osd_dev->scsi_device; |
| 296 | struct request_queue *q = scsi_device->request_queue; |
| 297 | struct request *req; |
| 298 | int ret = -ENOMEM; |
| 299 | |
| 300 | req = blk_get_request(q, has_out, flags); |
| 301 | if (!req) |
| 302 | goto out; |
| 303 | |
| 304 | or->request = req; |
| 305 | req->cmd_type = REQ_TYPE_BLOCK_PC; |
| 306 | req->timeout = or->timeout; |
| 307 | req->retries = or->retries; |
| 308 | req->sense = or->sense; |
| 309 | req->sense_len = 0; |
| 310 | |
| 311 | if (has_out) { |
| 312 | or->out.req = req; |
| 313 | if (has_in) { |
| 314 | /* allocate bidi request */ |
| 315 | req = blk_get_request(q, READ, flags); |
| 316 | if (!req) { |
| 317 | OSD_DEBUG("blk_get_request for bidi failed\n"); |
| 318 | goto out; |
| 319 | } |
| 320 | req->cmd_type = REQ_TYPE_BLOCK_PC; |
| 321 | or->in.req = or->request->next_rq = req; |
| 322 | } |
| 323 | } else if (has_in) |
| 324 | or->in.req = req; |
| 325 | |
| 326 | ret = 0; |
| 327 | out: |
| 328 | OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n", |
| 329 | or, has_in, has_out, ret, or->request); |
| 330 | return ret; |
| 331 | } |
| 332 | |
| 333 | int osd_finalize_request(struct osd_request *or, |
| 334 | u8 options, const void *cap, const u8 *cap_key) |
| 335 | { |
| 336 | struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb); |
| 337 | bool has_in, has_out; |
| 338 | int ret; |
| 339 | |
| 340 | if (options & OSD_REQ_FUA) |
| 341 | cdbh->options |= OSD_CDB_FUA; |
| 342 | |
| 343 | if (options & OSD_REQ_DPO) |
| 344 | cdbh->options |= OSD_CDB_DPO; |
| 345 | |
| 346 | if (options & OSD_REQ_BYPASS_TIMESTAMPS) |
| 347 | cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS; |
| 348 | |
| 349 | osd_set_caps(&or->cdb, cap); |
| 350 | |
| 351 | has_in = or->in.bio || or->get_attr.total_bytes; |
| 352 | has_out = or->out.bio || or->set_attr.total_bytes || |
| 353 | or->enc_get_attr.total_bytes; |
| 354 | |
| 355 | ret = _init_blk_request(or, has_in, has_out); |
| 356 | if (ret) { |
| 357 | OSD_DEBUG("_init_blk_request failed\n"); |
| 358 | return ret; |
| 359 | } |
| 360 | |
| 361 | if (or->out.bio) { |
| 362 | ret = blk_rq_append_bio(or->request->q, or->out.req, |
| 363 | or->out.bio); |
| 364 | if (ret) { |
| 365 | OSD_DEBUG("blk_rq_append_bio out failed\n"); |
| 366 | return ret; |
| 367 | } |
| 368 | OSD_DEBUG("out bytes=%llu (bytes_req=%u)\n", |
| 369 | _LLU(or->out.total_bytes), or->out.req->data_len); |
| 370 | } |
| 371 | if (or->in.bio) { |
| 372 | ret = blk_rq_append_bio(or->request->q, or->in.req, or->in.bio); |
| 373 | if (ret) { |
| 374 | OSD_DEBUG("blk_rq_append_bio in failed\n"); |
| 375 | return ret; |
| 376 | } |
| 377 | OSD_DEBUG("in bytes=%llu (bytes_req=%u)\n", |
| 378 | _LLU(or->in.total_bytes), or->in.req->data_len); |
| 379 | } |
| 380 | |
| 381 | if (!or->attributes_mode) |
| 382 | or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS; |
| 383 | cdbh->command_specific_options |= or->attributes_mode; |
| 384 | |
| 385 | or->request->cmd = or->cdb.buff; |
| 386 | or->request->cmd_len = _osd_req_cdb_len(or); |
| 387 | |
| 388 | return 0; |
| 389 | } |
| 390 | EXPORT_SYMBOL(osd_finalize_request); |
| 391 | |
| 392 | /* |
| 393 | * Implementation of osd_sec.h API |
| 394 | * TODO: Move to a separate osd_sec.c file at a later stage. |
| 395 | */ |
| 396 | |
| 397 | enum { OSD_SEC_CAP_V1_ALL_CAPS = |
| 398 | OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE | |
| 399 | OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR | |
| 400 | OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC | |
| 401 | OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT |
| 402 | }; |
| 403 | |
| 404 | void osd_sec_init_nosec_doall_caps(void *caps, |
| 405 | const struct osd_obj_id *obj, bool is_collection, const bool is_v1) |
| 406 | { |
| 407 | struct osd_capability *cap = caps; |
| 408 | u8 type; |
| 409 | u8 descriptor_type; |
| 410 | |
| 411 | if (likely(obj->id)) { |
| 412 | if (unlikely(is_collection)) { |
| 413 | type = OSD_SEC_OBJ_COLLECTION; |
| 414 | descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ : |
| 415 | OSD_SEC_OBJ_DESC_COL; |
| 416 | } else { |
| 417 | type = OSD_SEC_OBJ_USER; |
| 418 | descriptor_type = OSD_SEC_OBJ_DESC_OBJ; |
| 419 | } |
| 420 | WARN_ON(!obj->partition); |
| 421 | } else { |
| 422 | type = obj->partition ? OSD_SEC_OBJ_PARTITION : |
| 423 | OSD_SEC_OBJ_ROOT; |
| 424 | descriptor_type = OSD_SEC_OBJ_DESC_PAR; |
| 425 | } |
| 426 | |
| 427 | memset(cap, 0, sizeof(*cap)); |
| 428 | |
| 429 | cap->h.format = OSD_SEC_CAP_FORMAT_VER1; |
| 430 | cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */ |
| 431 | cap->h.security_method = OSD_SEC_NOSEC; |
| 432 | /* cap->expiration_time; |
| 433 | cap->AUDIT[30-10]; |
| 434 | cap->discriminator[42-30]; |
| 435 | cap->object_created_time; */ |
| 436 | cap->h.object_type = type; |
| 437 | osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS); |
| 438 | cap->h.object_descriptor_type = descriptor_type; |
| 439 | cap->od.obj_desc.policy_access_tag = 0; |
| 440 | cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition); |
| 441 | cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id); |
| 442 | } |
| 443 | EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps); |
| 444 | |
| 445 | void osd_set_caps(struct osd_cdb *cdb, const void *caps) |
| 446 | { |
| 447 | memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN); |
| 448 | } |