Free Electrons

Embedded Linux Experts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
/*
 * osd_initiator.h - OSD initiator API definition
 *
 * Copyright (C) 2008 Panasas Inc.  All rights reserved.
 *
 * Authors:
 *   Boaz Harrosh <bharrosh@panasas.com>
 *   Benny Halevy <bhalevy@panasas.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 *
 */
#ifndef __OSD_INITIATOR_H__
#define __OSD_INITIATOR_H__

#include "osd_protocol.h"
#include "osd_types.h"

#include <linux/blkdev.h>
#include <scsi/scsi_device.h>

/* Note: "NI" in comments below means "Not Implemented yet" */

/* Configure of code:
 * #undef if you *don't* want OSD v1 support in runtime.
 * If #defined the initiator will dynamically configure to encode OSD v1
 * CDB's if the target is detected to be OSD v1 only.
 * OSD v2 only commands, options, and attributes will be ignored if target
 * is v1 only.
 * If #defined will result in bigger/slower code (OK Slower maybe not)
 * Q: Should this be CONFIG_SCSI_OSD_VER1_SUPPORT and set from Kconfig?
 */
#define OSD_VER1_SUPPORT y

enum osd_std_version {
	OSD_VER_NONE = 0,
	OSD_VER1 = 1,
	OSD_VER2 = 2,
};

/*
 * Object-based Storage Device.
 * This object represents an OSD device.
 * It is not a full linux device in any way. It is only
 * a place to hang resources associated with a Linux
 * request Q and some default properties.
 */
struct osd_dev {
	struct scsi_device *scsi_device;
	unsigned def_timeout;

#ifdef OSD_VER1_SUPPORT
	enum osd_std_version version;
#endif
};

/* Unique Identification of an OSD device */
struct osd_dev_info {
	unsigned systemid_len;
	u8 systemid[OSD_SYSTEMID_LEN];
	unsigned osdname_len;
	u8 *osdname;
};

/* Retrieve/return osd_dev(s) for use by Kernel clients
 * Use IS_ERR/ERR_PTR on returned "osd_dev *".
 */
struct osd_dev *osduld_path_lookup(const char *dev_name);
struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi);
void osduld_put_device(struct osd_dev *od);

const struct osd_dev_info *osduld_device_info(struct osd_dev *od);
bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi);

/* Add/remove test ioctls from external modules */
typedef int (do_test_fn)(struct osd_dev *od, unsigned cmd, unsigned long arg);
int osduld_register_test(unsigned ioctl, do_test_fn *do_test);
void osduld_unregister_test(unsigned ioctl);

/* These are called by uld at probe time */
void osd_dev_init(struct osd_dev *od, struct scsi_device *scsi_device);
void osd_dev_fini(struct osd_dev *od);

/**
 * osd_auto_detect_ver - Detect the OSD version, return Unique Identification
 *
 * @od:     OSD target lun handle
 * @caps:   Capabilities authorizing OSD root read attributes access
 * @odi:    Retrieved information uniquely identifying the osd target lun
 *          Note: odi->osdname must be kfreed by caller.
 *
 * Auto detects the OSD version of the OSD target and sets the @od
 * accordingly. Meanwhile also returns the "system id" and "osd name" root
 * attributes which uniquely identify the OSD target. This member is usually
 * called by the ULD. ULD users should call osduld_device_info().
 * This rutine allocates osd requests and memory at GFP_KERNEL level and might
 * sleep.
 */
int osd_auto_detect_ver(struct osd_dev *od,
	void *caps, struct osd_dev_info *odi);

static inline struct request_queue *osd_request_queue(struct osd_dev *od)
{
	return od->scsi_device->request_queue;
}

/* we might want to use function vector in the future */
static inline void osd_dev_set_ver(struct osd_dev *od, enum osd_std_version v)
{
#ifdef OSD_VER1_SUPPORT
	od->version = v;
#endif
}

static inline bool osd_dev_is_ver1(struct osd_dev *od)
{
#ifdef OSD_VER1_SUPPORT
	return od->version == OSD_VER1;
#else
	return false;
#endif
}

struct osd_request;
typedef void (osd_req_done_fn)(struct osd_request *or, void *private);

struct osd_request {
	struct osd_cdb cdb;
	struct osd_data_out_integrity_info out_data_integ;
	struct osd_data_in_integrity_info in_data_integ;

	struct osd_dev *osd_dev;
	struct request *request;

	struct _osd_req_data_segment {
		void *buff;
		unsigned alloc_size; /* 0 here means: don't call kfree */
		unsigned total_bytes;
	} cdb_cont, set_attr, enc_get_attr, get_attr;

	struct _osd_io_info {
		struct bio *bio;
		u64 total_bytes;
		u64 residual;
		struct request *req;
		struct _osd_req_data_segment *last_seg;
		u8 *pad_buff;
	} out, in;

	gfp_t alloc_flags;
	unsigned timeout;
	unsigned retries;
	unsigned sense_len;
	u8 sense[OSD_MAX_SENSE_LEN];
	enum osd_attributes_mode attributes_mode;

	osd_req_done_fn *async_done;
	void *async_private;
	int async_error;
	int req_errors;
};

static inline bool osd_req_is_ver1(struct osd_request *or)
{
	return osd_dev_is_ver1(or->osd_dev);
}

/*
 * How to use the osd library:
 *
 * osd_start_request
 *	Allocates a request.
 *
 * osd_req_*
 *	Call one of, to encode the desired operation.
 *
 * osd_add_{get,set}_attr
 *	Optionally add attributes to the CDB, list or page mode.
 *
 * osd_finalize_request
 *	Computes final data out/in offsets and signs the request,
 *	making it ready for execution.
 *
 * osd_execute_request
 *	May be called to execute it through the block layer. Other wise submit
 *	the associated block request in some other way.
 *
 * After execution:
 * osd_req_decode_sense
 *	Decodes sense information to verify execution results.
 *
 * osd_req_decode_get_attr
 *	Retrieve osd_add_get_attr_list() values if used.
 *
 * osd_end_request
 *	Must be called to deallocate the request.
 */

/**
 * osd_start_request - Allocate and initialize an osd_request
 *
 * @osd_dev:    OSD device that holds the scsi-device and default values
 *              that the request is associated with.
 * @gfp:        The allocation flags to use for request allocation, and all
 *              subsequent allocations. This will be stored at
 *              osd_request->alloc_flags, can be changed by user later
 *
 * Allocate osd_request and initialize all members to the
 * default/initial state.
 */
struct osd_request *osd_start_request(struct osd_dev *od, gfp_t gfp);

enum osd_req_options {
	OSD_REQ_FUA = 0x08,	/* Force Unit Access */
	OSD_REQ_DPO = 0x10,	/* Disable Page Out */

	OSD_REQ_BYPASS_TIMESTAMPS = 0x80,
};

/**
 * osd_finalize_request - Sign request and prepare request for execution
 *
 * @or:		osd_request to prepare
 * @options:	combination of osd_req_options bit flags or 0.
 * @cap:	A Pointer to an OSD_CAP_LEN bytes buffer that is received from
 *              The security manager as capabilities for this cdb.
 * @cap_key:	The cryptographic key used to sign the cdb/data. Can be null
 *              if NOSEC is used.
 *
 * The actual request and bios are only allocated here, so are the get_attr
 * buffers that will receive the returned attributes. Copy's @cap to cdb.
 * Sign the cdb/data with @cap_key.
 */
int osd_finalize_request(struct osd_request *or,
	u8 options, const void *cap, const u8 *cap_key);

/**
 * osd_execute_request - Execute the request synchronously through block-layer
 *
 * @or:		osd_request to Executed
 *
 * Calls blk_execute_rq to q the command and waits for completion.
 */
int osd_execute_request(struct osd_request *or);

/**
 * osd_execute_request_async - Execute the request without waitting.
 *
 * @or:                      - osd_request to Executed
 * @done: (Optional)         - Called at end of execution
 * @private:                 - Will be passed to @done function
 *
 * Calls blk_execute_rq_nowait to queue the command. When execution is done
 * optionally calls @done with @private as parameter. @or->async_error will
 * have the return code
 */
int osd_execute_request_async(struct osd_request *or,
	osd_req_done_fn *done, void *private);

/**
 * osd_req_decode_sense_full - Decode sense information after execution.
 *
 * @or:           - osd_request to examine
 * @osi           - Recievs a more detailed error report information (optional).
 * @silent        - Do not print to dmsg (Even if enabled)
 * @bad_obj_list  - Some commands act on multiple objects. Failed objects will
 *                  be recieved here (optional)
 * @max_obj       - Size of @bad_obj_list.
 * @bad_attr_list - List of failing attributes (optional)
 * @max_attr      - Size of @bad_attr_list.
 *
 * After execution, osd_request results are analyzed using this function. The
 * return code is the final disposition on the error. So it is possible that a
 * CHECK_CONDITION was returned from target but this will return NO_ERROR, for
 * example on recovered errors. All parameters are optional if caller does
 * not need any returned information.
 * Note: This function will also dump the error to dmsg according to settings
 * of the SCSI_OSD_DPRINT_SENSE Kconfig value. Set @silent if you know the
 * command would routinely fail, to not spam the dmsg file.
 */

/**
 * osd_err_priority - osd categorized return codes in ascending severity.
 *
 * The categories are borrowed from the pnfs_osd_errno enum.
 * See comments for translated Linux codes returned by osd_req_decode_sense.
 */
enum osd_err_priority {
	OSD_ERR_PRI_NO_ERROR	= 0,
	/* Recoverable, caller should clear_highpage() all pages */
	OSD_ERR_PRI_CLEAR_PAGES = 1, /* -EFAULT */
	OSD_ERR_PRI_RESOURCE	= 2, /* -ENOMEM */
	OSD_ERR_PRI_BAD_CRED	= 3, /* -EINVAL */
	OSD_ERR_PRI_NO_ACCESS	= 4, /* -EACCES */
	OSD_ERR_PRI_UNREACHABLE	= 5, /* any other */
	OSD_ERR_PRI_NOT_FOUND	= 6, /* -ENOENT */
	OSD_ERR_PRI_NO_SPACE	= 7, /* -ENOSPC */
	OSD_ERR_PRI_EIO		= 8, /* -EIO    */
};

struct osd_sense_info {
	enum osd_err_priority osd_err_pri;

	int key;		/* one of enum scsi_sense_keys */
	int additional_code ;	/* enum osd_additional_sense_codes */
	union { /* Sense specific information */
		u16 sense_info;
		u16 cdb_field_offset; 	/* scsi_invalid_field_in_cdb */
	};
	union { /* Command specific information */
		u64 command_info;
	};

	u32 not_initiated_command_functions; /* osd_command_functions_bits */
	u32 completed_command_functions; /* osd_command_functions_bits */
	struct osd_obj_id obj;
	struct osd_attr attr;
};

int osd_req_decode_sense_full(struct osd_request *or,
	struct osd_sense_info *osi, bool silent,
	struct osd_obj_id *bad_obj_list, int max_obj,
	struct osd_attr *bad_attr_list, int max_attr);

static inline int osd_req_decode_sense(struct osd_request *or,
	struct osd_sense_info *osi)
{
	return osd_req_decode_sense_full(or, osi, false, NULL, 0, NULL, 0);
}

/**
 * osd_end_request - return osd_request to free store
 *
 * @or:		osd_request to free
 *
 * Deallocate all osd_request resources (struct req's, BIOs, buffers, etc.)
 */
void osd_end_request(struct osd_request *or);

/*
 * CDB Encoding
 *
 * Note: call only one of the following methods.
 */

/*
 * Device commands
 */
void osd_req_set_master_seed_xchg(struct osd_request *or, ...);/* NI */
void osd_req_set_master_key(struct osd_request *or, ...);/* NI */

void osd_req_format(struct osd_request *or, u64 tot_capacity);

/* list all partitions
 * @list header must be initialized to zero on first run.
 *
 * Call osd_is_obj_list_done() to find if we got the complete list.
 */
int osd_req_list_dev_partitions(struct osd_request *or,
	osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem);

void osd_req_flush_obsd(struct osd_request *or,
	enum osd_options_flush_scope_values);

void osd_req_perform_scsi_command(struct osd_request *or,
	const u8 *cdb, ...);/* NI */
void osd_req_task_management(struct osd_request *or, ...);/* NI */

/*
 * Partition commands
 */
void osd_req_create_partition(struct osd_request *or, osd_id partition);
void osd_req_remove_partition(struct osd_request *or, osd_id partition);

void osd_req_set_partition_key(struct osd_request *or,
	osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE],
	u8 seed[OSD_CRYPTO_SEED_SIZE]);/* NI */

/* list all collections in the partition
 * @list header must be init to zero on first run.
 *
 * Call osd_is_obj_list_done() to find if we got the complete list.
 */
int osd_req_list_partition_collections(struct osd_request *or,
	osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
	unsigned nelem);

/* list all objects in the partition
 * @list header must be init to zero on first run.
 *
 * Call osd_is_obj_list_done() to find if we got the complete list.
 */
int osd_req_list_partition_objects(struct osd_request *or,
	osd_id partition, osd_id initial_id, struct osd_obj_id_list *list,
	unsigned nelem);

void osd_req_flush_partition(struct osd_request *or,
	osd_id partition, enum osd_options_flush_scope_values);

/*
 * Collection commands
 */
void osd_req_create_collection(struct osd_request *or,
	const struct osd_obj_id *);/* NI */
void osd_req_remove_collection(struct osd_request *or,
	const struct osd_obj_id *);/* NI */

/* list all objects in the collection */
int osd_req_list_collection_objects(struct osd_request *or,
	const struct osd_obj_id *, osd_id initial_id,
	struct osd_obj_id_list *list, unsigned nelem);

/* V2 only filtered list of objects in the collection */
void osd_req_query(struct osd_request *or, ...);/* NI */

void osd_req_flush_collection(struct osd_request *or,
	const struct osd_obj_id *, enum osd_options_flush_scope_values);

void osd_req_get_member_attrs(struct osd_request *or, ...);/* V2-only NI */
void osd_req_set_member_attrs(struct osd_request *or, ...);/* V2-only NI */

/*
 * Object commands
 */
void osd_req_create_object(struct osd_request *or, struct osd_obj_id *);
void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *);

void osd_req_write(struct osd_request *or,
	const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len);
int osd_req_write_kern(struct osd_request *or,
	const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);
void osd_req_append(struct osd_request *or,
	const struct osd_obj_id *, struct bio *data_out);/* NI */
void osd_req_create_write(struct osd_request *or,
	const struct osd_obj_id *, struct bio *data_out, u64 offset);/* NI */
void osd_req_clear(struct osd_request *or,
	const struct osd_obj_id *, u64 offset, u64 len);/* NI */
void osd_req_punch(struct osd_request *or,
	const struct osd_obj_id *, u64 offset, u64 len);/* V2-only NI */

void osd_req_flush_object(struct osd_request *or,
	const struct osd_obj_id *, enum osd_options_flush_scope_values,
	/*V2*/ u64 offset, /*V2*/ u64 len);

void osd_req_read(struct osd_request *or,
	const struct osd_obj_id *obj, u64 offset, struct bio *bio, u64 len);
int osd_req_read_kern(struct osd_request *or,
	const struct osd_obj_id *obj, u64 offset, void *buff, u64 len);

/* Scatter/Gather write/read commands */
int osd_req_write_sg(struct osd_request *or,
	const struct osd_obj_id *obj, struct bio *bio,
	const struct osd_sg_entry *sglist, unsigned numentries);
int osd_req_read_sg(struct osd_request *or,
	const struct osd_obj_id *obj, struct bio *bio,
	const struct osd_sg_entry *sglist, unsigned numentries);
int osd_req_write_sg_kern(struct osd_request *or,
	const struct osd_obj_id *obj, void **buff,
	const struct osd_sg_entry *sglist, unsigned numentries);
int osd_req_read_sg_kern(struct osd_request *or,
	const struct osd_obj_id *obj, void **buff,
	const struct osd_sg_entry *sglist, unsigned numentries);

/*
 * Root/Partition/Collection/Object Attributes commands
 */

/* get before set */
void osd_req_get_attributes(struct osd_request *or, const struct osd_obj_id *);

/* set before get */
void osd_req_set_attributes(struct osd_request *or, const struct osd_obj_id *);

/*
 * Attributes appended to most commands
 */

/* Attributes List mode (or V2 CDB) */
  /*
   * TODO: In ver2 if at finalize time only one attr was set and no gets,
   * then the Attributes CDB mode is used automatically to save IO.
   */

/* set a list of attributes. */
int osd_req_add_set_attr_list(struct osd_request *or,
	const struct osd_attr *, unsigned nelem);

/* get a list of attributes */
int osd_req_add_get_attr_list(struct osd_request *or,
	const struct osd_attr *, unsigned nelem);

/*
 * Attributes list decoding
 * Must be called after osd_request.request was executed
 * It is called in a loop to decode the returned get_attr
 * (see osd_add_get_attr)
 */
int osd_req_decode_get_attr_list(struct osd_request *or,
	struct osd_attr *, int *nelem, void **iterator);

/* Attributes Page mode */

/*
 * Read an attribute page and optionally set one attribute
 *
 * Retrieves the attribute page directly to a user buffer.
 * @attr_page_data shall stay valid until end of execution.
 * See osd_attributes.h for common page structures
 */
int osd_req_add_get_attr_page(struct osd_request *or,
	u32 page_id, void *attr_page_data, unsigned max_page_len,
	const struct osd_attr *set_one);

#endif /* __OSD_LIB_H__ */