Commit 19fcae3d authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Martin K. Petersen

scsi: remove the SCSI OSD library

Now that all the users are gone the SCSI OSD library can be removed as
well.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarJens Axboe <axboe@kernel.dk>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 80f21213
The OSD Standard
================
OSD (Object-Based Storage Device) is a T10 SCSI command set that is designed
to provide efficient operation of input/output logical units that manage the
allocation, placement, and accessing of variable-size data-storage containers,
called objects. Objects are intended to contain operating system and application
constructs. Each object has associated attributes attached to it, which are
integral part of the object and provide metadata about the object. The standard
defines some common obligatory attributes, but user attributes can be added as
needed.
See: http://www.t10.org/ftp/t10/drafts/osd2/ for the latest draft for OSD 2
or search the web for "OSD SCSI"
OSD in the Linux Kernel
=======================
osd-initiator:
The main component of OSD in Kernel is the osd-initiator library. Its main
user is intended to be the pNFS-over-objects layout driver, which uses objects
as its back-end data storage. Other clients are the other osd parts listed below.
osd-uld:
This is a SCSI ULD that registers for OSD type devices and provides a testing
platform, both for the in-kernel initiator as well as connected targets. It
currently has no useful user-mode API, though it could have if need be.
osd target:
There are no current plans for an OSD target implementation in kernel. For all
needs, a user-mode target that is based on the scsi tgt target framework is
available from Ohio Supercomputer Center (OSC) at:
http://www.open-osd.org/bin/view/Main/OscOsdProject
There are several other target implementations. See http://open-osd.org for more
links.
Files and Folders
=================
This is the complete list of files included in this work:
include/scsi/
osd_initiator.h Main API for the initiator library
osd_types.h Common OSD types
osd_sec.h Security Manager API
osd_protocol.h Wire definitions of the OSD standard protocol
osd_attributes.h Wire definitions of OSD attributes
drivers/scsi/osd/
osd_initiator.c OSD-Initiator library implementation
osd_uld.c The OSD scsi ULD
osd_ktest.{h,c} In-kernel test suite (called by osd_uld)
osd_debug.h Some printk macros
Makefile For both in-tree and out-of-tree compilation
Kconfig Enables inclusion of the different pieces
osd_test.c User-mode application to call the kernel tests
The OSD-Initiator Library
=========================
osd_initiator is a low level implementation of an osd initiator encoder.
But even though, it should be intuitive and easy to use. Perhaps over time an
higher lever will form that automates some of the more common recipes.
init/fini:
- osd_dev_init() associates a scsi_device with an osd_dev structure
and initializes some global pools. This should be done once per scsi_device
(OSD LUN). The osd_dev structure is needed for calling osd_start_request().
- osd_dev_fini() cleans up before a osd_dev/scsi_device destruction.
OSD commands encoding, execution, and decoding of results:
struct osd_request's is used to iteratively encode an OSD command and carry
its state throughout execution. Each request goes through these stages:
a. osd_start_request() allocates the request.
b. Any of the osd_req_* methods is used to encode a request of the specified
type.
c. osd_req_add_{get,set}_attr_* may be called to add get/set attributes to the
CDB. "List" or "Page" mode can be used exclusively. The attribute-list API
can be called multiple times on the same request. However, only one
attribute-page can be read, as mandated by the OSD standard.
d. osd_finalize_request() computes offsets into the data-in and data-out buffers
and signs the request using the provided capability key and integrity-
check parameters.
e. osd_execute_request() may be called to execute the request via the block
layer and wait for its completion. The request can be executed
asynchronously by calling the block layer API directly.
f. After execution, osd_req_decode_sense() can be called to decode the request's
sense information.
g. osd_req_decode_get_attr() may be called to retrieve osd_add_get_attr_list()
values.
h. osd_end_request() must be called to deallocate the request and any resource
associated with it. Note that osd_end_request cleans up the request at any
stage and it must always be called after a successful osd_start_request().
osd_request's structure:
The OSD standard defines a complex structure of IO segments pointed to by
members in the CDB. Up to 3 segments can be deployed in the IN-Buffer and up to
4 in the OUT-Buffer. The ASCII illustration below depicts a secure-read with
associated get+set of attributes-lists. Other combinations very on the same
basic theme. From no-segments-used up to all-segments-used.
|________OSD-CDB__________|
| |
|read_len (offset=0) -|---------\
| | |
|get_attrs_list_length | |
|get_attrs_list_offset -|----\ |
| | | |
|retrieved_attrs_alloc_len| | |
|retrieved_attrs_offset -|----|----|-\
| | | | |
|set_attrs_list_length | | | |
|set_attrs_list_offset -|-\ | | |
| | | | | |
|in_data_integ_offset -|-|--|----|-|-\
|out_data_integ_offset -|-|--|--\ | | |
\_________________________/ | | | | | |
| | | | | |
|_______OUT-BUFFER________| | | | | | |
| Set attr list |</ | | | | |
| | | | | | |
|-------------------------| | | | | |
| Get attr descriptors |<---/ | | | |
| | | | | |
|-------------------------| | | | |
| Out-data integrity |<------/ | | |
| | | | |
\_________________________/ | | |
| | |
|________IN-BUFFER________| | | |
| In-Data read |<--------/ | |
| | | |
|-------------------------| | |
| Get attr list |<----------/ |
| | |
|-------------------------| |
| In-data integrity |<------------/
| |
\_________________________/
A block device request can carry bidirectional payload by means of associating
a bidi_read request with a main write-request. Each in/out request is described
by a chain of BIOs associated with each request.
The CDB is of a SCSI VARLEN CDB format, as described by OSD standard.
The OSD standard also mandates alignment restrictions at start of each segment.
In the code, in struct osd_request, there are two _osd_io_info structures to
describe the IN/OUT buffers above, two BIOs for the data payload and up to five
_osd_req_data_segment structures to hold the different segments allocation and
information.
Important: We have chosen to disregard the assumption that a BIO-chain (and
the resulting sg-list) describes a linear memory buffer. Meaning only first and
last scatter chain can be incomplete and all the middle chains are of PAGE_SIZE.
For us, a scatter-gather-list, as its name implies and as used by the Networking
layer, is to describe a vector of buffers that will be transferred to/from the
wire. It works very well with current iSCSI transport. iSCSI is currently the
only deployed OSD transport. In the future we anticipate SAS and FC attached OSD
devices as well.
The OSD Testing ULD
===================
TODO: More user-mode control on tests.
Authors, Mailing list
=====================
Please communicate with us on any deployment of osd, whether using this code
or not.
Any problems, questions, bug reports, lonely OSD nights, please email:
OSD Dev List <osd-dev@open-osd.org>
More up-to-date information can be found on:
http://open-osd.org
Boaz Harrosh <ooo@electrozaur.com>
References
==========
Weber, R., "SCSI Object-Based Storage Device Commands",
T10/1355-D ANSI/INCITS 400-2004,
http://www.t10.org/ftp/t10/drafts/osd/osd-r10.pdf
Weber, R., "SCSI Object-Based Storage Device Commands -2 (OSD-2)"
T10/1729-D, Working Draft, rev. 3
http://www.t10.org/ftp/t10/drafts/osd2/osd2r03.pdf
......@@ -11385,12 +11385,6 @@ W: http://www.nongnu.org/orinoco/
S: Orphan
F: drivers/net/wireless/intersil/orinoco/
OSD LIBRARY and FILESYSTEM
M: Boaz Harrosh <ooo@electrozaur.com>
S: Maintained
F: drivers/scsi/osd/
F: include/scsi/osd_*
OV2659 OMNIVISION SENSOR DRIVER
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
......
......@@ -1515,6 +1515,4 @@ source "drivers/scsi/pcmcia/Kconfig"
source "drivers/scsi/device_handler/Kconfig"
source "drivers/scsi/osd/Kconfig"
endmenu
......@@ -150,7 +150,6 @@ obj-$(CONFIG_CHR_DEV_SG) += sg.o
obj-$(CONFIG_CHR_DEV_SCH) += ch.o
obj-$(CONFIG_SCSI_ENCLOSURE) += ses.o
obj-$(CONFIG_SCSI_OSD_INITIATOR) += osd/
obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
# This goes last, so that "real" scsi devices probe earlier
......
#
# Kbuild for the OSD modules
#
# Copyright (C) 2008 Panasas Inc. All rights reserved.
#
# Authors:
# Boaz Harrosh <ooo@electrozaur.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
#
# libosd.ko - osd-initiator library
libosd-y := osd_initiator.o
obj-$(CONFIG_SCSI_OSD_INITIATOR) += libosd.o
# osd.ko - SCSI ULD and char-device
osd-y := osd_uld.o
obj-$(CONFIG_SCSI_OSD_ULD) += osd.o
#
# Kernel configuration file for the OSD scsi protocol
#
# Copyright (C) 2008 Panasas Inc. All rights reserved.
#
# Authors:
# Boaz Harrosh <ooo@electrozaur.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 version 2 License as
# published by the Free Software Foundation
#
config SCSI_OSD_INITIATOR
tristate "OSD-Initiator library"
depends on SCSI
help
Enable the OSD-Initiator library (libosd.ko).
NOTE: You must also select CRYPTO_SHA1 + CRYPTO_HMAC and their
dependencies
config SCSI_OSD_ULD
tristate "OSD Upper Level driver"
depends on SCSI_OSD_INITIATOR
help
Build a SCSI upper layer driver that exports /dev/osdX devices
to user-mode for testing and controlling OSD devices. It is also
needed by exofs, for mounting an OSD based file system.
config SCSI_OSD_DPRINT_SENSE
int "(0-2) When sense is returned, DEBUG print all sense descriptors"
default 1
depends on SCSI_OSD_INITIATOR
help
When a CHECK_CONDITION status is returned from a target, and a
sense-buffer is retrieved, turning this on will dump a full
sense-decoding message. Setting to 2 will also print recoverable
errors that might be regularly returned for some filesystem
operations.
config SCSI_OSD_DEBUG
bool "Compile All OSD modules with lots of DEBUG prints"
default n
depends on SCSI_OSD_INITIATOR
help
OSD Code is populated with lots of OSD_DEBUG(..) printouts to
dmesg. Enable this if you found a bug and you want to help us
track the problem (see also MAINTAINERS). Setting this will also
force SCSI_OSD_DPRINT_SENSE=2.
/*
* osd_debug.h - Some kprintf macros
*
* Copyright (C) 2008 Panasas Inc. All rights reserved.
*
* Authors:
* Boaz Harrosh <ooo@electrozaur.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_DEBUG_H__
#define __OSD_DEBUG_H__
#define OSD_ERR(fmt, a...) printk(KERN_ERR "osd: " fmt, ##a)
#define OSD_INFO(fmt, a...) printk(KERN_NOTICE "osd: " fmt, ##a)
#ifdef CONFIG_SCSI_OSD_DEBUG
#define OSD_DEBUG(fmt, a...) \
printk(KERN_NOTICE "osd @%s:%d: " fmt, __func__, __LINE__, ##a)
#else
#define OSD_DEBUG(fmt, a...) do {} while (0)
#endif
/* u64 has problems with printk this will cast it to unsigned long long */
#define _LLU(x) (unsigned long long)(x)
#endif /* ndef __OSD_DEBUG_H__ */
/*
* osd_initiator - Main body of the osd initiator library.
*
* Note: The file does not contain the advanced security functionality which
* is only needed by the security_manager's initiators.
*
* Copyright (C) 2008 Panasas Inc. All rights reserved.
*
* Authors:
* Boaz Harrosh <ooo@electrozaur.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
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Panasas company nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <scsi/osd_initiator.h>
#include <scsi/osd_sec.h>
#include <scsi/osd_attributes.h>
#include <scsi/osd_sense.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_request.h>
#include "osd_debug.h"
#ifndef __unused
# define __unused __attribute__((unused))
#endif
enum { OSD_REQ_RETRIES = 1 };
MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>");
MODULE_DESCRIPTION("open-osd initiator library libosd.ko");
MODULE_LICENSE("GPL");
static inline void build_test(void)
{
/* structures were not packed */
BUILD_BUG_ON(sizeof(struct osd_capability) != OSD_CAP_LEN);
BUILD_BUG_ON(sizeof(struct osdv2_cdb) != OSD_TOTAL_CDB_LEN);
BUILD_BUG_ON(sizeof(struct osdv1_cdb) != OSDv1_TOTAL_CDB_LEN);
}
static const char *_osd_ver_desc(struct osd_request *or)
{
return osd_req_is_ver1(or) ? "OSD1" : "OSD2";
}
#define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
static int _osd_get_print_system_info(struct osd_dev *od,
void *caps, struct osd_dev_info *odi)
{
struct osd_request *or;
struct osd_attr get_attrs[] = {
ATTR_DEF_RI(OSD_ATTR_RI_VENDOR_IDENTIFICATION, 8),
ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_IDENTIFICATION, 16),
ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_MODEL, 32),
ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_REVISION_LEVEL, 4),
ATTR_DEF_RI(OSD_ATTR_RI_PRODUCT_SERIAL_NUMBER, 64 /*variable*/),
ATTR_DEF_RI(OSD_ATTR_RI_OSD_NAME, 64 /*variable*/),
ATTR_DEF_RI(OSD_ATTR_RI_TOTAL_CAPACITY, 8),
ATTR_DEF_RI(OSD_ATTR_RI_USED_CAPACITY, 8),
ATTR_DEF_RI(OSD_ATTR_RI_NUMBER_OF_PARTITIONS, 8),
ATTR_DEF_RI(OSD_ATTR_RI_CLOCK, 6),
/* IBM-OSD-SIM Has a bug with this one put it last */
ATTR_DEF_RI(OSD_ATTR_RI_OSD_SYSTEM_ID, 20),
};
void *iter = NULL, *pFirst;
int nelem = ARRAY_SIZE(get_attrs), a = 0;
int ret;
or = osd_start_request(od);
if (!or)
return -ENOMEM;
/* get attrs */
osd_req_get_attributes(or, &osd_root_object);
osd_req_add_get_attr_list(or, get_attrs, ARRAY_SIZE(get_attrs));
ret = osd_finalize_request(or, 0, caps, NULL);
if (ret)
goto out;
ret = osd_execute_request(or);
if (ret) {
OSD_ERR("Failed to detect %s => %d\n", _osd_ver_desc(or), ret);
goto out;
}
osd_req_decode_get_attr_list(or, get_attrs, &nelem, &iter);
OSD_INFO("Detected %s device\n",
_osd_ver_desc(or));
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("VENDOR_IDENTIFICATION [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("PRODUCT_IDENTIFICATION [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("PRODUCT_MODEL [%s]\n",
(char *)pFirst);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("PRODUCT_REVISION_LEVEL [%u]\n",
pFirst ? get_unaligned_be32(pFirst) : ~0U);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("PRODUCT_SERIAL_NUMBER [%s]\n",
(char *)pFirst);
odi->osdname_len = get_attrs[a].len;
/* Avoid NULL for memcmp optimization 0-length is good enough */
odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
if (!odi->osdname) {
ret = -ENOMEM;
goto out;
}
if (odi->osdname_len)
memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
OSD_INFO("OSD_NAME [%s]\n", odi->osdname);
a++;
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("TOTAL_CAPACITY [0x%llx]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("USED_CAPACITY [0x%llx]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("NUMBER_OF_PARTITIONS [%llu]\n",
pFirst ? _LLU(get_unaligned_be64(pFirst)) : ~0ULL);
if (a >= nelem)
goto out;
/* FIXME: Where are the time utilities */
pFirst = get_attrs[a++].val_ptr;
OSD_INFO("CLOCK [0x%6phN]\n", pFirst);
if (a < nelem) { /* IBM-OSD-SIM bug, Might not have it */
unsigned len = get_attrs[a].len;
char sid_dump[32*4 + 2]; /* 2nibbles+space+ASCII */
hex_dump_to_buffer(get_attrs[a].val_ptr, len, 32, 1,
sid_dump, sizeof(sid_dump), true);
OSD_INFO("OSD_SYSTEM_ID(%d)\n"
" [%s]\n", len, sid_dump);
if (unlikely(len > sizeof(odi->systemid))) {
OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
"device identification might not work\n", len);
len = sizeof(odi->systemid);
}
odi->systemid_len = len;
memcpy(odi->systemid, get_attrs[a].val_ptr, len);
a++;
}
out:
osd_end_request(or);
return ret;
}
int osd_auto_detect_ver(struct osd_dev *od,
void *caps, struct osd_dev_info *odi)
{
int ret;
/* Auto-detect the osd version */
ret = _osd_get_print_system_info(od, caps, odi);
if (ret) {
osd_dev_set_ver(od, OSD_VER1);
OSD_DEBUG("converting to OSD1\n");
ret = _osd_get_print_system_info(od, caps, odi);
}
return ret;
}
EXPORT_SYMBOL(osd_auto_detect_ver);
static unsigned _osd_req_cdb_len(struct osd_request *or)
{
return osd_req_is_ver1(or) ? OSDv1_TOTAL_CDB_LEN : OSD_TOTAL_CDB_LEN;
}
static unsigned _osd_req_alist_elem_size(struct osd_request *or, unsigned len)
{
return osd_req_is_ver1(or) ?
osdv1_attr_list_elem_size(len) :
osdv2_attr_list_elem_size(len);
}
static void _osd_req_alist_elem_encode(struct osd_request *or,
void *attr_last, const struct osd_attr *oa)
{
if (osd_req_is_ver1(or)) {
struct osdv1_attributes_list_element *attr = attr_last;
attr->attr_page = cpu_to_be32(oa->attr_page);
attr->attr_id = cpu_to_be32(oa->attr_id);
attr->attr_bytes = cpu_to_be16(oa->len);
memcpy(attr->attr_val, oa->val_ptr, oa->len);
} else {
struct osdv2_attributes_list_element *attr = attr_last;
attr->attr_page = cpu_to_be32(oa->attr_page);
attr->attr_id = cpu_to_be32(oa->attr_id);
attr->attr_bytes = cpu_to_be16(oa->len);
memcpy(attr->attr_val, oa->val_ptr, oa->len);
}
}
static int _osd_req_alist_elem_decode(struct osd_request *or,
void *cur_p, struct osd_attr *oa, unsigned max_bytes)
{
unsigned inc;
if (osd_req_is_ver1(or)) {
struct osdv1_attributes_list_element *attr = cur_p;
if (max_bytes < sizeof(*attr))
return -1;
oa->len = be16_to_cpu(attr->attr_bytes);
inc = _osd_req_alist_elem_size(or, oa->len);
if (inc > max_bytes)
return -1;
oa->attr_page = be32_to_cpu(attr->attr_page);
oa->attr_id = be32_to_cpu(attr->attr_id);
/* OSD1: On empty attributes we return a pointer to 2 bytes
* of zeros. This keeps similar behaviour with OSD2.
* (See below)
*/
oa->val_ptr = likely(oa->len) ? attr->attr_val :
(u8 *)&attr->attr_bytes;
} else {
struct osdv2_attributes_list_element *attr = cur_p;
if (max_bytes < sizeof(*attr))
return -1;
oa->len = be16_to_cpu(attr->attr_bytes);
inc = _osd_req_alist_elem_size(or, oa->len);
if (inc > max_bytes)
return -1;
oa->attr_page = be32_to_cpu(attr->attr_page);
oa->attr_id = be32_to_cpu(attr->attr_id);
/* OSD2: For convenience, on empty attributes, we return 8 bytes
* of zeros here. This keeps the same behaviour with OSD2r04,
* and is nice with null terminating ASCII fields.
* oa->val_ptr == NULL marks the end-of-list, or error.
*/
oa->val_ptr = likely(oa->len) ? attr->attr_val : attr->reserved;
}
return inc;
}
static unsigned _osd_req_alist_size(struct osd_request *or, void *list_head)
{
return osd_req_is_ver1(or) ?
osdv1_list_size(list_head) :
osdv2_list_size(list_head);
}
static unsigned _osd_req_sizeof_alist_header(struct osd_request *or)
{
return osd_req_is_ver1(or) ?
sizeof(struct osdv1_attributes_list_header) :
sizeof(struct osdv2_attributes_list_header);
}
static void _osd_req_set_alist_type(struct osd_request *or,
void *list, int list_type)
{
if (osd_req_is_ver1(or)) {
struct osdv1_attributes_list_header *attr_list = list;
memset(attr_list, 0, sizeof(*attr_list));
attr_list->type = list_type;
} else {
struct osdv2_attributes_list_header *attr_list = list;
memset(attr_list, 0, sizeof(*attr_list));
attr_list->type = list_type;
}
}
static bool _osd_req_is_alist_type(struct osd_request *or,
void *list, int list_type)
{
if (!list)
return false;
if (osd_req_is_ver1(or)) {
struct osdv1_attributes_list_header *attr_list = list;
return attr_list->type == list_type;
} else {
struct osdv2_attributes_list_header *attr_list = list;
return attr_list->type == list_type;
}
}
/* This is for List-objects not Attributes-Lists */
static void _osd_req_encode_olist(struct osd_request *or,
struct osd_obj_id_list *list)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
if (osd_req_is_ver1(or)) {
cdbh->v1.list_identifier = list->list_identifier;
cdbh->v1.start_address = list->continuation_id;
} else {
cdbh->v2.list_identifier = list->list_identifier;
cdbh->v2.start_address = list->continuation_id;
}
}
static osd_cdb_offset osd_req_encode_offset(struct osd_request *or,
u64 offset, unsigned *padding)
{
return __osd_encode_offset(offset, padding,
osd_req_is_ver1(or) ?
OSDv1_OFFSET_MIN_SHIFT : OSD_OFFSET_MIN_SHIFT,
OSD_OFFSET_MAX_SHIFT);
}
static struct osd_security_parameters *
_osd_req_sec_params(struct osd_request *or)
{
struct osd_cdb *ocdb = &or->cdb;
if (osd_req_is_ver1(or))
return (struct osd_security_parameters *)&ocdb->v1.sec_params;
else
return (struct osd_security_parameters *)&ocdb->v2.sec_params;
}
void osd_dev_init(struct osd_dev *osdd, struct scsi_device *scsi_device)
{
memset(osdd, 0, sizeof(*osdd));
osdd->scsi_device = scsi_device;
osdd->def_timeout = BLK_DEFAULT_SG_TIMEOUT;
#ifdef OSD_VER1_SUPPORT
osdd->version = OSD_VER2;
#endif
/* TODO: Allocate pools for osd_request attributes ... */
}
EXPORT_SYMBOL(osd_dev_init);
void osd_dev_fini(struct osd_dev *osdd)
{
/* TODO: De-allocate pools */
osdd->scsi_device = NULL;
}
EXPORT_SYMBOL(osd_dev_fini);
static struct osd_request *_osd_request_alloc(gfp_t gfp)
{
struct osd_request *or;
/* TODO: Use mempool with one saved request */
or = kzalloc(sizeof(*or), gfp);
return or;
}
static void _osd_request_free(struct osd_request *or)
{
kfree(or);
}
struct osd_request *osd_start_request(struct osd_dev *dev)
{
struct osd_request *or;
or = _osd_request_alloc(GFP_KERNEL);
if (!or)
return NULL;
or->osd_dev = dev;
or->timeout = dev->def_timeout;
or->retries = OSD_REQ_RETRIES;
return or;
}
EXPORT_SYMBOL(osd_start_request);
static void _osd_free_seg(struct osd_request *or __unused,
struct _osd_req_data_segment *seg)
{
if (!seg->buff || !seg->alloc_size)
return;
kfree(seg->buff);
seg->buff = NULL;
seg->alloc_size = 0;
}
static void _put_request(struct request *rq)
{
/*
* If osd_finalize_request() was called but the request was not
* executed through the block layer, then we must release BIOs.
* TODO: Keep error code in or->async_error. Need to audit all
* code paths.
*/
if (unlikely(rq->bio))
blk_mq_end_request(rq, BLK_STS_IOERR);
else
blk_put_request(rq);
}
void osd_end_request(struct osd_request *or)
{
struct request *rq = or->request;
if (rq) {
if (rq->next_rq) {
_put_request(rq->next_rq);
rq->next_rq = NULL;
}
_put_request(rq);
}
_osd_free_seg(or, &or->get_attr);
_osd_free_seg(or, &or->enc_get_attr);
_osd_free_seg(or, &or->set_attr);
_osd_free_seg(or, &or->cdb_cont);
_osd_request_free(or);
}
EXPORT_SYMBOL(osd_end_request);
static void _set_error_resid(struct osd_request *or, struct request *req,
blk_status_t error)
{
or->async_error = error;
or->req_errors = scsi_req(req)->result;
or->sense_len = scsi_req(req)->sense_len;
if (or->sense_len)
memcpy(or->sense, scsi_req(req)->sense, or->sense_len);
if (or->out.req)
or->out.residual = scsi_req(or->out.req)->resid_len;
if (or->in.req)
or->in.residual = scsi_req(or->in.req)->resid_len;
}
int osd_execute_request(struct osd_request *or)
{
blk_execute_rq(or->request->q, NULL, or->request, 0);
if (scsi_req(or->request)->result) {
_set_error_resid(or, or->request, BLK_STS_IOERR);
return -EIO;
}
_set_error_resid(or, or->request, BLK_STS_OK);
return 0;
}
EXPORT_SYMBOL(osd_execute_request);
static void osd_request_async_done(struct request *req, blk_status_t error)
{
struct osd_request *or = req->end_io_data;
_set_error_resid(or, req, error);
if (req->next_rq) {
blk_put_request(req->next_rq);
req->next_rq = NULL;
}
blk_put_request(req);
or->request = NULL;
or->in.req = NULL;
or->out.req = NULL;
if (or->async_done)
or->async_done(or, or->async_private);
else
osd_end_request(or);
}
int osd_execute_request_async(struct osd_request *or,
osd_req_done_fn *done, void *private)
{
or->request->end_io_data = or;
or->async_private = private;
or->async_done = done;
blk_execute_rq_nowait(or->request->q, NULL, or->request, 0,
osd_request_async_done);
return 0;
}
EXPORT_SYMBOL(osd_execute_request_async);
u8 sg_out_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
u8 sg_in_pad_buffer[1 << OSDv1_OFFSET_MIN_SHIFT];
static int _osd_realloc_seg(struct osd_request *or,
struct _osd_req_data_segment *seg, unsigned max_bytes)
{
void *buff;
if (seg->alloc_size >= max_bytes)
return 0;
buff = krealloc(seg->buff, max_bytes, GFP_KERNEL);
if (!buff) {
OSD_ERR("Failed to Realloc %d-bytes was-%d\n", max_bytes,
seg->alloc_size);
return -ENOMEM;
}
memset(buff + seg->alloc_size, 0, max_bytes - seg->alloc_size);
seg->buff = buff;
seg->alloc_size = max_bytes;
return 0;
}
static int _alloc_cdb_cont(struct osd_request *or, unsigned total_bytes)
{
OSD_DEBUG("total_bytes=%d\n", total_bytes);
return _osd_realloc_seg(or, &or->cdb_cont, total_bytes);
}
static int _alloc_set_attr_list(struct osd_request *or,
const struct osd_attr *oa, unsigned nelem, unsigned add_bytes)
{
unsigned total_bytes = add_bytes;
for (; nelem; --nelem, ++oa)
total_bytes += _osd_req_alist_elem_size(or, oa->len);
OSD_DEBUG("total_bytes=%d\n", total_bytes);
return _osd_realloc_seg(or, &or->set_attr, total_bytes);
}
static int _alloc_get_attr_desc(struct osd_request *or, unsigned max_bytes)
{
OSD_DEBUG("total_bytes=%d\n", max_bytes);
return _osd_realloc_seg(or, &or->enc_get_attr, max_bytes);
}
static int _alloc_get_attr_list(struct osd_request *or)
{
OSD_DEBUG("total_bytes=%d\n", or->get_attr.total_bytes);
return _osd_realloc_seg(or, &or->get_attr, or->get_attr.total_bytes);
}
/*
* Common to all OSD commands
*/
static void _osdv1_req_encode_common(struct osd_request *or,
__be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
{
struct osdv1_cdb *ocdb = &or->cdb.v1;
/*
* For speed, the commands
* OSD_ACT_PERFORM_SCSI_COMMAND , V1 0x8F7E, V2 0x8F7C
* OSD_ACT_SCSI_TASK_MANAGEMENT , V1 0x8F7F, V2 0x8F7D
* are not supported here. Should pass zero and set after the call
*/
act &= cpu_to_be16(~0x0080); /* V1 action code */
OSD_DEBUG("OSDv1 execute opcode 0x%x\n", be16_to_cpu(act));
ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
ocdb->h.varlen_cdb.service_action = act;
ocdb->h.partition = cpu_to_be64(obj->partition);
ocdb->h.object = cpu_to_be64(obj->id);
ocdb->h.v1.length = cpu_to_be64(len);
ocdb->h.v1.start_address = cpu_to_be64(offset);
}
static void _osdv2_req_encode_common(struct osd_request *or,
__be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
{
struct osdv2_cdb *ocdb = &or->cdb.v2;
OSD_DEBUG("OSDv2 execute opcode 0x%x\n", be16_to_cpu(act));
ocdb->h.varlen_cdb.opcode = VARIABLE_LENGTH_CMD;
ocdb->h.varlen_cdb.additional_cdb_length = OSD_ADDITIONAL_CDB_LENGTH;
ocdb->h.varlen_cdb.service_action = act;
ocdb->h.partition = cpu_to_be64(obj->partition);
ocdb->h.object = cpu_to_be64(obj->id);
ocdb->h.v2.length = cpu_to_be64(len);
ocdb->h.v2.start_address = cpu_to_be64(offset);
}
static void _osd_req_encode_common(struct osd_request *or,
__be16 act, const struct osd_obj_id *obj, u64 offset, u64 len)
{
if (osd_req_is_ver1(or))
_osdv1_req_encode_common(or, act, obj, offset, len);
else
_osdv2_req_encode_common(or, act, obj, offset, len);
}
/*
* Device commands
*/
/*TODO: void osd_req_set_master_seed_xchg(struct osd_request *, ...); */
/*TODO: void osd_req_set_master_key(struct osd_request *, ...); */
void osd_req_format(struct osd_request *or, u64 tot_capacity)
{
_osd_req_encode_common(or, OSD_ACT_FORMAT_OSD, &osd_root_object, 0,
tot_capacity);
}
EXPORT_SYMBOL(osd_req_format);
int osd_req_list_dev_partitions(struct osd_request *or,
osd_id initial_id, struct osd_obj_id_list *list, unsigned nelem)
{
return osd_req_list_partition_objects(or, 0, initial_id, list, nelem);
}
EXPORT_SYMBOL(osd_req_list_dev_partitions);
static void _osd_req_encode_flush(struct osd_request *or,
enum osd_options_flush_scope_values op)
{
struct osd_cdb_head *ocdb = osd_cdb_head(&or->cdb);
ocdb->command_specific_options = op;
}
void osd_req_flush_obsd(struct osd_request *or,
enum osd_options_flush_scope_values op)
{
_osd_req_encode_common(or, OSD_ACT_FLUSH_OSD, &osd_root_object, 0, 0);
_osd_req_encode_flush(or, op);
}
EXPORT_SYMBOL(osd_req_flush_obsd);
/*TODO: void osd_req_perform_scsi_command(struct osd_request *,
const u8 *cdb, ...); */
/*TODO: void osd_req_task_management(struct osd_request *, ...); */
/*
* Partition commands
*/
static void _osd_req_encode_partition(struct osd_request *or,
__be16 act, osd_id partition)
{
struct osd_obj_id par = {
.partition = partition,
.id = 0,
};
_osd_req_encode_common(or, act, &par, 0, 0);
}
void osd_req_create_partition(struct osd_request *or, osd_id partition)
{
_osd_req_encode_partition(or, OSD_ACT_CREATE_PARTITION, partition);
}
EXPORT_SYMBOL(osd_req_create_partition);
void osd_req_remove_partition(struct osd_request *or, osd_id partition)
{
_osd_req_encode_partition(or, OSD_ACT_REMOVE_PARTITION, partition);
}
EXPORT_SYMBOL(osd_req_remove_partition);
/*TODO: void osd_req_set_partition_key(struct osd_request *,
osd_id partition, u8 new_key_id[OSD_CRYPTO_KEYID_SIZE],
u8 seed[OSD_CRYPTO_SEED_SIZE]); */
static int _osd_req_list_objects(struct osd_request *or,
__be16 action, const struct osd_obj_id *obj, osd_id initial_id,
struct osd_obj_id_list *list, unsigned nelem)
{
struct request_queue *q = osd_request_queue(or->osd_dev);
u64 len = nelem * sizeof(osd_id) + sizeof(*list);
struct bio *bio;
_osd_req_encode_common(or, action, obj, (u64)initial_id, len);
if (list->list_identifier)
_osd_req_encode_olist(or, list);
WARN_ON(or->in.bio);
bio = bio_map_kern(q, list, len, GFP_KERNEL);
if (IS_ERR(bio)) {
OSD_ERR("!!! Failed to allocate list_objects BIO\n");
return PTR_ERR(bio);
}
bio_set_op_attrs(bio, REQ_OP_READ, 0);
or->in.bio = bio;
or->in.total_bytes = bio->bi_iter.bi_size;
return 0;
}
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)
{
struct osd_obj_id par = {
.partition = partition,
.id = 0,
};
return osd_req_list_collection_objects(or, &par, initial_id, list,
nelem);
}
EXPORT_SYMBOL(osd_req_list_partition_collections);
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)
{
struct osd_obj_id par = {
.partition = partition,
.id = 0,
};
return _osd_req_list_objects(or, OSD_ACT_LIST, &par, initial_id, list,
nelem);
}
EXPORT_SYMBOL(osd_req_list_partition_objects);
void osd_req_flush_partition(struct osd_request *or,
osd_id partition, enum osd_options_flush_scope_values op)
{
_osd_req_encode_partition(or, OSD_ACT_FLUSH_PARTITION, partition);
_osd_req_encode_flush(or, op);
}
EXPORT_SYMBOL(osd_req_flush_partition);
/*
* Collection commands
*/
/*TODO: void osd_req_create_collection(struct osd_request *,
const struct osd_obj_id *); */
/*TODO: void osd_req_remove_collection(struct osd_request *,
const struct osd_obj_id *); */
int osd_req_list_collection_objects(struct osd_request *or,
const struct osd_obj_id *obj, osd_id initial_id,
struct osd_obj_id_list *list, unsigned nelem)
{
return _osd_req_list_objects(or, OSD_ACT_LIST_COLLECTION, obj,
initial_id, list, nelem);
}
EXPORT_SYMBOL(osd_req_list_collection_objects);
/*TODO: void query(struct osd_request *, ...); V2 */
void osd_req_flush_collection(struct osd_request *or,
const struct osd_obj_id *obj, enum osd_options_flush_scope_values op)
{
_osd_req_encode_common(or, OSD_ACT_FLUSH_PARTITION, obj, 0, 0);
_osd_req_encode_flush(or, op);
}
EXPORT_SYMBOL(osd_req_flush_collection);
/*TODO: void get_member_attrs(struct osd_request *, ...); V2 */
/*TODO: void set_member_attrs(struct osd_request *, ...); V2 */
/*
* Object commands
*/
void osd_req_create_object(struct osd_request *or, struct osd_obj_id *obj)
{
_osd_req_encode_common(or, OSD_ACT_CREATE, obj, 0, 0);
}
EXPORT_SYMBOL(osd_req_create_object);
void osd_req_remove_object(struct osd_request *or, struct osd_obj_id *obj)
{
_osd_req_encode_common(or, OSD_ACT_REMOVE, obj, 0, 0);
}
EXPORT_SYMBOL(osd_req_remove_object);
/*TODO: void osd_req_create_multi(struct osd_request *or,
struct osd_obj_id *first, struct osd_obj_id_list *list, unsigned nelem);
*/
void osd_req_write(struct osd_request *or,
const struct osd_obj_id *obj, u64 offset,
struct bio *bio, u64 len)
{
_osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len);
WARN_ON(or->out.bio || or->out.total_bytes);
WARN_ON(!op_is_write(bio_op(bio)));
or->out.bio = bio;
or->out.total_bytes = len;
}
EXPORT_SYMBOL(osd_req_write);
int osd_req_write_kern(struct osd_request *or,
const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
{
struct request_queue *req_q = osd_request_queue(or->osd_dev);
struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
if (IS_ERR(bio))
return PTR_ERR(bio);
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
osd_req_write(or, obj, offset, bio, len);
return 0;
}
EXPORT_SYMBOL(osd_req_write_kern);
/*TODO: void osd_req_append(struct osd_request *,
const struct osd_obj_id *, struct bio *data_out); */
/*TODO: void osd_req_create_write(struct osd_request *,
const struct osd_obj_id *, struct bio *data_out, u64 offset); */
/*TODO: void osd_req_clear(struct osd_request *,
const struct osd_obj_id *, u64 offset, u64 len); */
/*TODO: void osd_req_punch(struct osd_request *,
const struct osd_obj_id *, u64 offset, u64 len); V2 */
void osd_req_flush_object(struct osd_request *or,
const struct osd_obj_id *obj, enum osd_options_flush_scope_values op,
/*V2*/ u64 offset, /*V2*/ u64 len)
{
if (unlikely(osd_req_is_ver1(or) && (offset || len))) {
OSD_DEBUG("OSD Ver1 flush on specific range ignored\n");
offset = 0;
len = 0;
}
_osd_req_encode_common(or, OSD_ACT_FLUSH, obj, offset, len);
_osd_req_encode_flush(or, op);
}
EXPORT_SYMBOL(osd_req_flush_object);
void osd_req_read(struct osd_request *or,
const struct osd_obj_id *obj, u64 offset,
struct bio *bio, u64 len)
{
_osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len);
WARN_ON(or->in.bio || or->in.total_bytes);
WARN_ON(op_is_write(bio_op(bio)));
or->in.bio = bio;
or->in.total_bytes = len;
}
EXPORT_SYMBOL(osd_req_read);
int osd_req_read_kern(struct osd_request *or,
const struct osd_obj_id *obj, u64 offset, void* buff, u64 len)
{
struct request_queue *req_q = osd_request_queue(or->osd_dev);
struct bio *bio = bio_map_kern(req_q, buff, len, GFP_KERNEL);
if (IS_ERR(bio))
return PTR_ERR(bio);
osd_req_read(or, obj, offset, bio, len);
return 0;
}
EXPORT_SYMBOL(osd_req_read_kern);
static int _add_sg_continuation_descriptor(struct osd_request *or,
const struct osd_sg_entry *sglist, unsigned numentries, u64 *len)
{
struct osd_sg_continuation_descriptor *oscd;
u32 oscd_size;
unsigned i;
int ret;
oscd_size = sizeof(*oscd) + numentries * sizeof(oscd->entries[0]);
if (!or->cdb_cont.total_bytes) {
/* First time, jump over the header, we will write to:
* cdb_cont.buff + cdb_cont.total_bytes
*/
or->cdb_cont.total_bytes =
sizeof(struct osd_continuation_segment_header);
}
ret = _alloc_cdb_cont(or, or->cdb_cont.total_bytes + oscd_size);
if (unlikely(ret))
return ret;
oscd = or->cdb_cont.buff + or->cdb_cont.total_bytes;
oscd->hdr.type = cpu_to_be16(SCATTER_GATHER_LIST);
oscd->hdr.pad_length = 0;
oscd->hdr.length = cpu_to_be32(oscd_size - sizeof(*oscd));
*len = 0;
/* copy the sg entries and convert to network byte order */
for (i = 0; i < numentries; i++) {
oscd->entries[i].offset = cpu_to_be64(sglist[i].offset);
oscd->entries[i].len = cpu_to_be64(sglist[i].len);
*len += sglist[i].len;
}
or->cdb_cont.total_bytes += oscd_size;
OSD_DEBUG("total_bytes=%d oscd_size=%d numentries=%d\n",
or->cdb_cont.total_bytes, oscd_size, numentries);
return 0;
}
static int _osd_req_finalize_cdb_cont(struct osd_request *or, const u8 *cap_key)
{
struct request_queue *req_q = osd_request_queue(or->osd_dev);
struct bio *bio;
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
struct osd_continuation_segment_header *cont_seg_hdr;
if (!or->cdb_cont.total_bytes)
return 0;
cont_seg_hdr = or->cdb_cont.buff;
cont_seg_hdr->format = CDB_CONTINUATION_FORMAT_V2;
cont_seg_hdr->service_action = cdbh->varlen_cdb.service_action;
/* create a bio for continuation segment */
bio = bio_map_kern(req_q, or->cdb_cont.buff, or->cdb_cont.total_bytes,
GFP_KERNEL);
if (IS_ERR(bio))
return PTR_ERR(bio);
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
/* integrity check the continuation before the bio is linked
* with the other data segments since the continuation
* integrity is separate from the other data segments.
*/
osd_sec_sign_data(cont_seg_hdr->integrity_check, bio, cap_key);
cdbh->v2.cdb_continuation_length = cpu_to_be32(or->cdb_cont.total_bytes);
/* we can't use _req_append_segment, because we need to link in the
* continuation bio to the head of the bio list - the
* continuation segment (if it exists) is always the first segment in
* the out data buffer.
*/
bio->bi_next = or->out.bio;
or->out.bio = bio;
or->out.total_bytes += or->cdb_cont.total_bytes;
return 0;
}
/* osd_req_write_sg: Takes a @bio that points to the data out buffer and an
* @sglist that has the scatter gather entries. Scatter-gather enables a write
* of multiple none-contiguous areas of an object, in a single call. The extents
* may overlap and/or be in any order. The only constrain is that:
* total_bytes(sglist) >= total_bytes(bio)
*/
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)
{
u64 len;
int ret = _add_sg_continuation_descriptor(or, sglist, numentries, &len);
if (ret)
return ret;
osd_req_write(or, obj, 0, bio, len);
return 0;
}
EXPORT_SYMBOL(osd_req_write_sg);
/* osd_req_read_sg: Read multiple extents of an object into @bio
* See osd_req_write_sg
*/
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)
{
u64 len;
u64 off;
int ret;
if (numentries > 1) {
off = 0;
ret = _add_sg_continuation_descriptor(or, sglist, numentries,
&len);
if (ret)
return ret;
} else {
/* Optimize the case of single segment, read_sg is a
* bidi operation.
*/
len = sglist->len;
off = sglist->offset;
}
osd_req_read(or, obj, off, bio, len);
return 0;
}
EXPORT_SYMBOL(osd_req_read_sg);
/* SG-list write/read Kern API
*
* osd_req_{write,read}_sg_kern takes an array of @buff pointers and an array
* of sg_entries. @numentries indicates how many pointers and sg_entries there
* are. By requiring an array of buff pointers. This allows a caller to do a
* single write/read and scatter into multiple buffers.
* NOTE: Each buffer + len should not cross a page boundary.
*/
static struct bio *_create_sg_bios(struct osd_request *or,
void **buff, const struct osd_sg_entry *sglist, unsigned numentries)
{
struct request_queue *q = osd_request_queue(or->osd_dev);
struct bio *bio;
unsigned i;
bio = bio_kmalloc(GFP_KERNEL, numentries);
if (unlikely(!bio)) {
OSD_DEBUG("Failed to allocate BIO size=%u\n", numentries);
return ERR_PTR(-ENOMEM);
}
for (i = 0; i < numentries; i++) {
unsigned offset = offset_in_page(buff[i]);
struct page *page = virt_to_page(buff[i]);
unsigned len = sglist[i].len;
unsigned added_len;
BUG_ON(offset + len > PAGE_SIZE);
added_len = bio_add_pc_page(q, bio, page, len, offset);
if (unlikely(len != added_len)) {
OSD_DEBUG("bio_add_pc_page len(%d) != added_len(%d)\n",
len, added_len);
bio_put(bio);
return ERR_PTR(-ENOMEM);
}
}
return bio;
}
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)
{
struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
if (IS_ERR(bio))
return PTR_ERR(bio);
bio_set_op_attrs(bio, REQ_OP_WRITE, 0);
osd_req_write_sg(or, obj, bio, sglist, numentries);
return 0;
}
EXPORT_SYMBOL(osd_req_write_sg_kern);
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)
{
struct bio *bio = _create_sg_bios(or, buff, sglist, numentries);
if (IS_ERR(bio))
return PTR_ERR(bio);
osd_req_read_sg(or, obj, bio, sglist, numentries);
return 0;
}
EXPORT_SYMBOL(osd_req_read_sg_kern);
void osd_req_get_attributes(struct osd_request *or,
const struct osd_obj_id *obj)
{
_osd_req_encode_common(or, OSD_ACT_GET_ATTRIBUTES, obj, 0, 0);
}
EXPORT_SYMBOL(osd_req_get_attributes);
void osd_req_set_attributes(struct osd_request *or,
const struct osd_obj_id *obj)
{
_osd_req_encode_common(or, OSD_ACT_SET_ATTRIBUTES, obj, 0, 0);
}
EXPORT_SYMBOL(osd_req_set_attributes);
/*
* Attributes List-mode
*/
int osd_req_add_set_attr_list(struct osd_request *or,
const struct osd_attr *oa, unsigned nelem)
{
unsigned total_bytes = or->set_attr.total_bytes;
void *attr_last;
int ret;
if (or->attributes_mode &&
or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
WARN_ON(1);
return -EINVAL;
}
or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
if (!total_bytes) { /* first-time: allocate and put list header */
total_bytes = _osd_req_sizeof_alist_header(or);
ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
if (ret)
return ret;
_osd_req_set_alist_type(or, or->set_attr.buff,
OSD_ATTR_LIST_SET_RETRIEVE);
}
attr_last = or->set_attr.buff + total_bytes;
for (; nelem; --nelem) {
unsigned elem_size = _osd_req_alist_elem_size(or, oa->len);
total_bytes += elem_size;
if (unlikely(or->set_attr.alloc_size < total_bytes)) {
or->set_attr.total_bytes = total_bytes - elem_size;
ret = _alloc_set_attr_list(or, oa, nelem, total_bytes);
if (ret)
return ret;
attr_last =
or->set_attr.buff + or->set_attr.total_bytes;
}
_osd_req_alist_elem_encode(or, attr_last, oa);
attr_last += elem_size;
++oa;
}
or->set_attr.total_bytes = total_bytes;
return 0;
}
EXPORT_SYMBOL(osd_req_add_set_attr_list);
static int _req_append_segment(struct osd_request *or,
unsigned padding, struct _osd_req_data_segment *seg,
struct _osd_req_data_segment *last_seg, struct _osd_io_info *io)
{
void *pad_buff;
int ret;
if (padding) {
/* check if we can just add it to last buffer */
if (last_seg &&
(padding <= last_seg->alloc_size - last_seg->total_bytes))
pad_buff = last_seg->buff + last_seg->total_bytes;
else
pad_buff = io->pad_buff;
ret = blk_rq_map_kern(io->req->q, io->req, pad_buff, padding,
GFP_KERNEL);
if (ret)
return ret;
io->total_bytes += padding;
}
ret = blk_rq_map_kern(io->req->q, io->req, seg->buff, seg->total_bytes,
GFP_KERNEL);
if (ret)
return ret;
io->total_bytes += seg->total_bytes;
OSD_DEBUG("padding=%d buff=%p total_bytes=%d\n", padding, seg->buff,
seg->total_bytes);
return 0;
}
static int _osd_req_finalize_set_attr_list(struct osd_request *or)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
unsigned padding;
int ret;
if (!or->set_attr.total_bytes) {
cdbh->attrs_list.set_attr_offset = OSD_OFFSET_UNUSED;
return 0;
}
cdbh->attrs_list.set_attr_bytes = cpu_to_be32(or->set_attr.total_bytes);
cdbh->attrs_list.set_attr_offset =
osd_req_encode_offset(or, or->out.total_bytes, &padding);
ret = _req_append_segment(or, padding, &or->set_attr,
or->out.last_seg, &or->out);
if (ret)
return ret;
or->out.last_seg = &or->set_attr;
return 0;
}
int osd_req_add_get_attr_list(struct osd_request *or,
const struct osd_attr *oa, unsigned nelem)
{
unsigned total_bytes = or->enc_get_attr.total_bytes;
void *attr_last;
int ret;
if (or->attributes_mode &&
or->attributes_mode != OSD_CDB_GET_SET_ATTR_LISTS) {
WARN_ON(1);
return -EINVAL;
}
or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
/* first time calc data-in list header size */
if (!or->get_attr.total_bytes)
or->get_attr.total_bytes = _osd_req_sizeof_alist_header(or);
/* calc data-out info */
if (!total_bytes) { /* first-time: allocate and put list header */
unsigned max_bytes;
total_bytes = _osd_req_sizeof_alist_header(or);
max_bytes = total_bytes +
nelem * sizeof(struct osd_attributes_list_attrid);
ret = _alloc_get_attr_desc(or, max_bytes);
if (ret)
return ret;
_osd_req_set_alist_type(or, or->enc_get_attr.buff,
OSD_ATTR_LIST_GET);
}
attr_last = or->enc_get_attr.buff + total_bytes;
for (; nelem; --nelem) {
struct osd_attributes_list_attrid *attrid;
const unsigned cur_size = sizeof(*attrid);
total_bytes += cur_size;
if (unlikely(or->enc_get_attr.alloc_size < total_bytes)) {
or->enc_get_attr.total_bytes = total_bytes - cur_size;
ret = _alloc_get_attr_desc(or,
total_bytes + nelem * sizeof(*attrid));
if (ret)
return ret;
attr_last = or->enc_get_attr.buff +
or->enc_get_attr.total_bytes;
}
attrid = attr_last;
attrid->attr_page = cpu_to_be32(oa->attr_page);
attrid->attr_id = cpu_to_be32(oa->attr_id);
attr_last += cur_size;
/* calc data-in size */
or->get_attr.total_bytes +=
_osd_req_alist_elem_size(or, oa->len);
++oa;
}
or->enc_get_attr.total_bytes = total_bytes;
OSD_DEBUG(
"get_attr.total_bytes=%u(%u) enc_get_attr.total_bytes=%u(%zu)\n",
or->get_attr.total_bytes,
or->get_attr.total_bytes - _osd_req_sizeof_alist_header(or),
or->enc_get_attr.total_bytes,
(or->enc_get_attr.total_bytes - _osd_req_sizeof_alist_header(or))
/ sizeof(struct osd_attributes_list_attrid));
return 0;
}
EXPORT_SYMBOL(osd_req_add_get_attr_list);
static int _osd_req_finalize_get_attr_list(struct osd_request *or)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
unsigned out_padding;
unsigned in_padding;
int ret;
if (!or->enc_get_attr.total_bytes) {
cdbh->attrs_list.get_attr_desc_offset = OSD_OFFSET_UNUSED;
cdbh->attrs_list.get_attr_offset = OSD_OFFSET_UNUSED;
return 0;
}
ret = _alloc_get_attr_list(or);
if (ret)
return ret;
/* The out-going buffer info update */
OSD_DEBUG("out-going\n");
cdbh->attrs_list.get_attr_desc_bytes =
cpu_to_be32(or->enc_get_attr.total_bytes);
cdbh->attrs_list.get_attr_desc_offset =
osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
ret = _req_append_segment(or, out_padding, &or->enc_get_attr,
or->out.last_seg, &or->out);
if (ret)
return ret;
or->out.last_seg = &or->enc_get_attr;
/* The incoming buffer info update */
OSD_DEBUG("in-coming\n");
cdbh->attrs_list.get_attr_alloc_length =
cpu_to_be32(or->get_attr.total_bytes);
cdbh->attrs_list.get_attr_offset =
osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
&or->in);
if (ret)
return ret;
or->in.last_seg = &or->get_attr;
return 0;
}
int osd_req_decode_get_attr_list(struct osd_request *or,
struct osd_attr *oa, int *nelem, void **iterator)
{
unsigned cur_bytes, returned_bytes;
int n;
const unsigned sizeof_attr_list = _osd_req_sizeof_alist_header(or);
void *cur_p;
if (!_osd_req_is_alist_type(or, or->get_attr.buff,
OSD_ATTR_LIST_SET_RETRIEVE)) {
oa->attr_page = 0;
oa->attr_id = 0;
oa->val_ptr = NULL;
oa->len = 0;
*iterator = NULL;
return 0;
}
if (*iterator) {
BUG_ON((*iterator < or->get_attr.buff) ||
(or->get_attr.buff + or->get_attr.alloc_size < *iterator));
cur_p = *iterator;
cur_bytes = (*iterator - or->get_attr.buff) - sizeof_attr_list;
returned_bytes = or->get_attr.total_bytes;
} else { /* first time decode the list header */
cur_bytes = sizeof_attr_list;
returned_bytes = _osd_req_alist_size(or, or->get_attr.buff) +
sizeof_attr_list;
cur_p = or->get_attr.buff + sizeof_attr_list;
if (returned_bytes > or->get_attr.alloc_size) {
OSD_DEBUG("target report: space was not big enough! "
"Allocate=%u Needed=%u\n",
or->get_attr.alloc_size,
returned_bytes + sizeof_attr_list);
returned_bytes =
or->get_attr.alloc_size - sizeof_attr_list;
}
or->get_attr.total_bytes = returned_bytes;
}
for (n = 0; (n < *nelem) && (cur_bytes < returned_bytes); ++n) {
int inc = _osd_req_alist_elem_decode(or, cur_p, oa,
returned_bytes - cur_bytes);
if (inc < 0) {
OSD_ERR("BAD FOOD from target. list not valid!"
"c=%d r=%d n=%d\n",
cur_bytes, returned_bytes, n);
oa->val_ptr = NULL;
cur_bytes = returned_bytes; /* break the caller loop */
break;
}
cur_bytes += inc;
cur_p += inc;
++oa;
}
*iterator = (returned_bytes - cur_bytes) ? cur_p : NULL;
*nelem = n;
return returned_bytes - cur_bytes;
}
EXPORT_SYMBOL(osd_req_decode_get_attr_list);
/*
* Attributes Page-mode
*/
int osd_req_add_get_attr_page(struct osd_request *or,
u32 page_id, void *attar_page, unsigned max_page_len,
const struct osd_attr *set_one_attr)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
if (or->attributes_mode &&
or->attributes_mode != OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
WARN_ON(1);
return -EINVAL;
}
or->attributes_mode = OSD_CDB_GET_ATTR_PAGE_SET_ONE;
or->get_attr.buff = attar_page;
or->get_attr.total_bytes = max_page_len;
cdbh->attrs_page.get_attr_page = cpu_to_be32(page_id);
cdbh->attrs_page.get_attr_alloc_length = cpu_to_be32(max_page_len);
if (!set_one_attr || !set_one_attr->attr_page)
return 0; /* The set is optional */
or->set_attr.buff = set_one_attr->val_ptr;
or->set_attr.total_bytes = set_one_attr->len;
cdbh->attrs_page.set_attr_page = cpu_to_be32(set_one_attr->attr_page);
cdbh->attrs_page.set_attr_id = cpu_to_be32(set_one_attr->attr_id);
cdbh->attrs_page.set_attr_length = cpu_to_be32(set_one_attr->len);
return 0;
}
EXPORT_SYMBOL(osd_req_add_get_attr_page);
static int _osd_req_finalize_attr_page(struct osd_request *or)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
unsigned in_padding, out_padding;
int ret;
/* returned page */
cdbh->attrs_page.get_attr_offset =
osd_req_encode_offset(or, or->in.total_bytes, &in_padding);
ret = _req_append_segment(or, in_padding, &or->get_attr, NULL,
&or->in);
if (ret)
return ret;
if (or->set_attr.total_bytes == 0)
return 0;
/* set one value */
cdbh->attrs_page.set_attr_offset =
osd_req_encode_offset(or, or->out.total_bytes, &out_padding);
ret = _req_append_segment(or, out_padding, &or->set_attr, NULL,
&or->out);
return ret;
}
static inline void osd_sec_parms_set_out_offset(bool is_v1,
struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
{
if (is_v1)
sec_parms->v1.data_out_integrity_check_offset = offset;
else
sec_parms->v2.data_out_integrity_check_offset = offset;
}
static inline void osd_sec_parms_set_in_offset(bool is_v1,
struct osd_security_parameters *sec_parms, osd_cdb_offset offset)
{
if (is_v1)
sec_parms->v1.data_in_integrity_check_offset = offset;
else
sec_parms->v2.data_in_integrity_check_offset = offset;
}
static int _osd_req_finalize_data_integrity(struct osd_request *or,
bool has_in, bool has_out, struct bio *out_data_bio, u64 out_data_bytes,
const u8 *cap_key)
{
struct osd_security_parameters *sec_parms = _osd_req_sec_params(or);
int ret;
if (!osd_is_sec_alldata(sec_parms))
return 0;
if (has_out) {
struct _osd_req_data_segment seg = {
.buff = &or->out_data_integ,
.total_bytes = sizeof(or->out_data_integ),
};
unsigned pad;
or->out_data_integ.data_bytes = cpu_to_be64(out_data_bytes);
or->out_data_integ.set_attributes_bytes = cpu_to_be64(
or->set_attr.total_bytes);
or->out_data_integ.get_attributes_bytes = cpu_to_be64(
or->enc_get_attr.total_bytes);
osd_sec_parms_set_out_offset(osd_req_is_ver1(or), sec_parms,
osd_req_encode_offset(or, or->out.total_bytes, &pad));
ret = _req_append_segment(or, pad, &seg, or->out.last_seg,
&or->out);
if (ret)
return ret;
or->out.last_seg = NULL;
/* they are now all chained to request sign them all together */
osd_sec_sign_data(&or->out_data_integ, out_data_bio,
cap_key);
}
if (has_in) {
struct _osd_req_data_segment seg = {
.buff = &or->in_data_integ,
.total_bytes = sizeof(or->in_data_integ),
};
unsigned pad;
osd_sec_parms_set_in_offset(osd_req_is_ver1(or), sec_parms,
osd_req_encode_offset(or, or->in.total_bytes, &pad));
ret = _req_append_segment(or, pad, &seg, or->in.last_seg,
&or->in);
if (ret)
return ret;
or->in.last_seg = NULL;
}
return 0;
}
/*
* osd_finalize_request and helpers
*/
static struct request *_make_request(struct request_queue *q, bool has_write,
struct _osd_io_info *oii)
{
struct request *req;
struct bio *bio = oii->bio;
int ret;
req = blk_get_request(q, has_write ? REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN,
0);
if (IS_ERR(req))
return req;
for_each_bio(bio) {
struct bio *bounce_bio = bio;
ret = blk_rq_append_bio(req, &bounce_bio);
if (ret)
return ERR_PTR(ret);
}
return req;
}
static int _init_blk_request(struct osd_request *or,
bool has_in, bool has_out)
{
struct scsi_device *scsi_device = or->osd_dev->scsi_device;
struct request_queue *q = scsi_device->request_queue;
struct request *req;
int ret;
req = _make_request(q, has_out, has_out ? &or->out : &or->in);
if (IS_ERR(req)) {
ret = PTR_ERR(req);
goto out;
}
or->request = req;
req->rq_flags |= RQF_QUIET;
req->timeout = or->timeout;
scsi_req(req)->retries = or->retries;
if (has_out) {
or->out.req = req;
if (has_in) {
/* allocate bidi request */
req = _make_request(q, false, &or->in);
if (IS_ERR(req)) {
OSD_DEBUG("blk_get_request for bidi failed\n");
ret = PTR_ERR(req);
goto out;
}
or->in.req = or->request->next_rq = req;
}
} else if (has_in)
or->in.req = req;
ret = 0;
out:
OSD_DEBUG("or=%p has_in=%d has_out=%d => %d, %p\n",
or, has_in, has_out, ret, or->request);
return ret;
}
int osd_finalize_request(struct osd_request *or,
u8 options, const void *cap, const u8 *cap_key)
{
struct osd_cdb_head *cdbh = osd_cdb_head(&or->cdb);
bool has_in, has_out;
/* Save for data_integrity without the cdb_continuation */
struct bio *out_data_bio = or->out.bio;
u64 out_data_bytes = or->out.total_bytes;
int ret;
if (options & OSD_REQ_FUA)
cdbh->options |= OSD_CDB_FUA;
if (options & OSD_REQ_DPO)
cdbh->options |= OSD_CDB_DPO;
if (options & OSD_REQ_BYPASS_TIMESTAMPS)
cdbh->timestamp_control = OSD_CDB_BYPASS_TIMESTAMPS;
osd_set_caps(&or->cdb, cap);
has_in = or->in.bio || or->get_attr.total_bytes;
has_out = or->out.bio || or->cdb_cont.total_bytes ||
or->set_attr.total_bytes || or->enc_get_attr.total_bytes;
ret = _osd_req_finalize_cdb_cont(or, cap_key);
if (ret) {
OSD_DEBUG("_osd_req_finalize_cdb_cont failed\n");
return ret;
}
ret = _init_blk_request(or, has_in, has_out);
if (ret) {
OSD_DEBUG("_init_blk_request failed\n");
return ret;
}
or->out.pad_buff = sg_out_pad_buffer;
or->in.pad_buff = sg_in_pad_buffer;
if (!or->attributes_mode)
or->attributes_mode = OSD_CDB_GET_SET_ATTR_LISTS;
cdbh->command_specific_options |= or->attributes_mode;
if (or->attributes_mode == OSD_CDB_GET_ATTR_PAGE_SET_ONE) {
ret = _osd_req_finalize_attr_page(or);
if (ret) {
OSD_DEBUG("_osd_req_finalize_attr_page failed\n");
return ret;
}
} else {
/* TODO: I think that for the GET_ATTR command these 2 should
* be reversed to keep them in execution order (for embedded
* targets with low memory footprint)
*/
ret = _osd_req_finalize_set_attr_list(or);
if (ret) {
OSD_DEBUG("_osd_req_finalize_set_attr_list failed\n");
return ret;
}
ret = _osd_req_finalize_get_attr_list(or);
if (ret) {
OSD_DEBUG("_osd_req_finalize_get_attr_list failed\n");
return ret;
}
}
ret = _osd_req_finalize_data_integrity(or, has_in, has_out,
out_data_bio, out_data_bytes,
cap_key);
if (ret)
return ret;
osd_sec_sign_cdb(&or->cdb, cap_key);
scsi_req(or->request)->cmd = or->cdb.buff;
scsi_req(or->request)->cmd_len = _osd_req_cdb_len(or);
return 0;
}
EXPORT_SYMBOL(osd_finalize_request);
static bool _is_osd_security_code(int code)
{
return (code == osd_security_audit_value_frozen) ||
(code == osd_security_working_key_frozen) ||
(code == osd_nonce_not_unique) ||
(code == osd_nonce_timestamp_out_of_range) ||
(code == osd_invalid_dataout_buffer_integrity_check_value);
}
#define OSD_SENSE_PRINT1(fmt, a...) \
do { \
if (__cur_sense_need_output) \
OSD_ERR(fmt, ##a); \
} while (0)
#define OSD_SENSE_PRINT2(fmt, a...) OSD_SENSE_PRINT1(" " fmt, ##a)
int osd_req_decode_sense_full(struct osd_request *or,
struct osd_sense_info *osi, bool silent,
struct osd_obj_id *bad_obj_list __unused, int max_obj __unused,
struct osd_attr *bad_attr_list, int max_attr)
{
int sense_len, original_sense_len;
struct osd_sense_info local_osi;
struct scsi_sense_descriptor_based *ssdb;
void *cur_descriptor;
#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 0)
const bool __cur_sense_need_output = false;
#else
bool __cur_sense_need_output = !silent;
#endif
int ret;
if (likely(!or->req_errors))
return 0;
osi = osi ? : &local_osi;
memset(osi, 0, sizeof(*osi));
ssdb = (typeof(ssdb))or->sense;
sense_len = or->sense_len;
if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) {
OSD_ERR("Block-layer returned error(0x%x) but "
"sense_len(%u) || key(%d) is empty\n",
or->req_errors, sense_len, ssdb->sense_key);
goto analyze;
}
if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
ssdb->response_code, sense_len);
goto analyze;
}
osi->key = ssdb->sense_key;
osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
original_sense_len = ssdb->additional_sense_length + 8;
#if (CONFIG_SCSI_OSD_DPRINT_SENSE == 1)
if (__cur_sense_need_output)
__cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
#endif
OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
"additional_code=0x%x async_error=%d errors=0x%x\n",
osi->key, original_sense_len, sense_len,
osi->additional_code, or->async_error,
or->req_errors);
if (original_sense_len < sense_len)
sense_len = original_sense_len;
cur_descriptor = ssdb->ssd;
sense_len -= sizeof(*ssdb);
while (sense_len > 0) {
struct scsi_sense_descriptor *ssd = cur_descriptor;
int cur_len = ssd->additional_length + 2;
sense_len -= cur_len;
if (sense_len < 0)
break; /* sense was truncated */
switch (ssd->descriptor_type) {
case scsi_sense_information:
case scsi_sense_command_specific_information:
{
struct scsi_sense_command_specific_data_descriptor
*sscd = cur_descriptor;
osi->command_info =
get_unaligned_be64(&sscd->information) ;
OSD_SENSE_PRINT2(
"command_specific_information 0x%llx \n",
_LLU(osi->command_info));
break;
}
case scsi_sense_key_specific:
{
struct scsi_sense_key_specific_data_descriptor
*ssks = cur_descriptor;
osi->sense_info = get_unaligned_be16(&ssks->value);
OSD_SENSE_PRINT2(
"sense_key_specific_information %u"
"sksv_cd_bpv_bp (0x%x)\n",
osi->sense_info, ssks->sksv_cd_bpv_bp);
break;
}
case osd_sense_object_identification:
{ /*FIXME: Keep first not last, Store in array*/
struct osd_sense_identification_data_descriptor
*osidd = cur_descriptor;
osi->not_initiated_command_functions =
le32_to_cpu(osidd->not_initiated_functions);
osi->completed_command_functions =
le32_to_cpu(osidd->completed_functions);
osi->obj.partition = be64_to_cpu(osidd->partition_id);
osi->obj.id = be64_to_cpu(osidd->object_id);
OSD_SENSE_PRINT2(
"object_identification pid=0x%llx oid=0x%llx\n",
_LLU(osi->obj.partition), _LLU(osi->obj.id));
OSD_SENSE_PRINT2(
"not_initiated_bits(%x) "
"completed_command_bits(%x)\n",
osi->not_initiated_command_functions,
osi->completed_command_functions);
break;
}
case osd_sense_response_integrity_check:
{
struct osd_sense_response_integrity_check_descriptor
*d = cur_descriptor;
/* 2nibbles+space+ASCII */
char dump[sizeof(d->integrity_check_value) * 4 + 2];
hex_dump_to_buffer(d->integrity_check_value,
sizeof(d->integrity_check_value),
32, 1, dump, sizeof(dump), true);
OSD_SENSE_PRINT2("response_integrity [%s]\n", dump);
}
case osd_sense_attribute_identification:
{
struct osd_sense_attributes_data_descriptor
*osadd = cur_descriptor;
unsigned len = min(cur_len, sense_len);
struct osd_sense_attr *pattr = osadd->sense_attrs;
while (len >= sizeof(*pattr)) {
u32 attr_page = be32_to_cpu(pattr->attr_page);
u32 attr_id = be32_to_cpu(pattr->attr_id);
if (!osi->attr.attr_page) {
osi->attr.attr_page = attr_page;
osi->attr.attr_id = attr_id;
}
if (bad_attr_list && max_attr) {
bad_attr_list->attr_page = attr_page;
bad_attr_list->attr_id = attr_id;
bad_attr_list++;
max_attr--;
}
len -= sizeof(*pattr);
OSD_SENSE_PRINT2(
"osd_sense_attribute_identification"
"attr_page=0x%x attr_id=0x%x\n",
attr_page, attr_id);
}
}
/*These are not legal for OSD*/
case scsi_sense_field_replaceable_unit:
OSD_SENSE_PRINT2("scsi_sense_field_replaceable_unit\n");
break;
case scsi_sense_stream_commands:
OSD_SENSE_PRINT2("scsi_sense_stream_commands\n");
break;
case scsi_sense_block_commands:
OSD_SENSE_PRINT2("scsi_sense_block_commands\n");
break;
case scsi_sense_ata_return:
OSD_SENSE_PRINT2("scsi_sense_ata_return\n");
break;
default:
if (ssd->descriptor_type <= scsi_sense_Reserved_last)
OSD_SENSE_PRINT2(
"scsi_sense Reserved descriptor (0x%x)",
ssd->descriptor_type);
else
OSD_SENSE_PRINT2(
"scsi_sense Vendor descriptor (0x%x)",
ssd->descriptor_type);
}
cur_descriptor += cur_len;
}
analyze:
if (!osi->key) {
/* scsi sense is Empty, the request was never issued to target
* linux return code might tell us what happened.
*/
if (or->async_error == BLK_STS_RESOURCE)
osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
else
osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
ret = or->async_error;
} else if (osi->key <= scsi_sk_recovered_error) {
osi->osd_err_pri = 0;
ret = 0;
} else if (osi->additional_code == scsi_invalid_field_in_cdb) {
if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
ret = -EFAULT; /* caller should recover from this */
} else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
ret = -ENOENT;
} else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
ret = -EACCES;
} else {
osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
ret = -EINVAL;
}
} else if (osi->additional_code == osd_quota_error) {
osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
ret = -ENOSPC;
} else if (_is_osd_security_code(osi->additional_code)) {
osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
ret = -EINVAL;
} else {
osi->osd_err_pri = OSD_ERR_PRI_EIO;
ret = -EIO;
}
if (!or->out.residual)
or->out.residual = or->out.total_bytes;
if (!or->in.residual)
or->in.residual = or->in.total_bytes;
return ret;
}
EXPORT_SYMBOL(osd_req_decode_sense_full);
/*
* Implementation of osd_sec.h API
* TODO: Move to a separate osd_sec.c file at a later stage.
*/
enum { OSD_SEC_CAP_V1_ALL_CAPS =
OSD_SEC_CAP_APPEND | OSD_SEC_CAP_OBJ_MGMT | OSD_SEC_CAP_REMOVE |
OSD_SEC_CAP_CREATE | OSD_SEC_CAP_SET_ATTR | OSD_SEC_CAP_GET_ATTR |
OSD_SEC_CAP_WRITE | OSD_SEC_CAP_READ | OSD_SEC_CAP_POL_SEC |
OSD_SEC_CAP_GLOBAL | OSD_SEC_CAP_DEV_MGMT
};
enum { OSD_SEC_CAP_V2_ALL_CAPS =
OSD_SEC_CAP_V1_ALL_CAPS | OSD_SEC_CAP_QUERY | OSD_SEC_CAP_M_OBJECT
};
void osd_sec_init_nosec_doall_caps(void *caps,
const struct osd_obj_id *obj, bool is_collection, const bool is_v1)
{
struct osd_capability *cap = caps;
u8 type;
u8 descriptor_type;
if (likely(obj->id)) {
if (unlikely(is_collection)) {
type = OSD_SEC_OBJ_COLLECTION;
descriptor_type = is_v1 ? OSD_SEC_OBJ_DESC_OBJ :
OSD_SEC_OBJ_DESC_COL;
} else {
type = OSD_SEC_OBJ_USER;
descriptor_type = OSD_SEC_OBJ_DESC_OBJ;
}
WARN_ON(!obj->partition);
} else {
type = obj->partition ? OSD_SEC_OBJ_PARTITION :
OSD_SEC_OBJ_ROOT;
descriptor_type = OSD_SEC_OBJ_DESC_PAR;
}
memset(cap, 0, sizeof(*cap));
cap->h.format = OSD_SEC_CAP_FORMAT_VER1;
cap->h.integrity_algorithm__key_version = 0; /* MAKE_BYTE(0, 0); */
cap->h.security_method = OSD_SEC_NOSEC;
/* cap->expiration_time;
cap->AUDIT[30-10];
cap->discriminator[42-30];
cap->object_created_time; */
cap->h.object_type = type;
osd_sec_set_caps(&cap->h, OSD_SEC_CAP_V1_ALL_CAPS);
cap->h.object_descriptor_type = descriptor_type;
cap->od.obj_desc.policy_access_tag = 0;
cap->od.obj_desc.allowed_partition_id = cpu_to_be64(obj->partition);
cap->od.obj_desc.allowed_object_id = cpu_to_be64(obj->id);
}
EXPORT_SYMBOL(osd_sec_init_nosec_doall_caps);
/* FIXME: Extract version from caps pointer.
* Also Pete's target only supports caps from OSDv1 for now
*/
void osd_set_caps(struct osd_cdb *cdb, const void *caps)
{
/* NOTE: They start at same address */
memcpy(&cdb->v1.caps, caps, OSDv1_CAP_LEN);
}
bool osd_is_sec_alldata(struct osd_security_parameters *sec_parms __unused)
{
return false;
}
void osd_sec_sign_cdb(struct osd_cdb *ocdb __unused, const u8 *cap_key __unused)
{
}
void osd_sec_sign_data(void *data_integ __unused,
struct bio *bio __unused, const u8 *cap_key __unused)
{
}
/*
* Declared in osd_protocol.h
* 4.12.5 Data-In and Data-Out buffer offsets
* byte offset = mantissa * (2^(exponent+8))
* Returns the smallest allowed encoded offset that contains given @offset
* The actual encoded offset returned is @offset + *@padding.
*/
osd_cdb_offset __osd_encode_offset(
u64 offset, unsigned *padding, int min_shift, int max_shift)
{
u64 try_offset = -1, mod, align;
osd_cdb_offset be32_offset;
int shift;
*padding = 0;
if (!offset)
return 0;
for (shift = min_shift; shift < max_shift; ++shift) {
try_offset = offset >> shift;
if (try_offset < (1 << OSD_OFFSET_MAX_BITS))
break;
}
BUG_ON(shift == max_shift);
align = 1 << shift;
mod = offset & (align - 1);
if (mod) {
*padding = align - mod;
try_offset += 1;
}
try_offset |= ((shift - 8) & 0xf) << 28;
be32_offset = cpu_to_be32((u32)try_offset);
OSD_DEBUG("offset=%llu mantissa=%llu exp=%d encoded=%x pad=%d\n",
_LLU(offset), _LLU(try_offset & 0x0FFFFFFF), shift,
be32_offset, *padding);
return be32_offset;
}
/*
* osd_uld.c - OSD Upper Layer Driver
*
* A Linux driver module that registers as a SCSI ULD and probes
* for OSD type SCSI devices.
* It's main function is to export osd devices to in-kernel users like
* osdfs and pNFS-objects-LD. It also provides one ioctl for running
* in Kernel tests.
*
* Copyright (C) 2008 Panasas Inc. All rights reserved.
*
* Authors:
* Boaz Harrosh <ooo@electrozaur.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
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Panasas company nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/namei.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/major.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <scsi/scsi.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/osd_initiator.h>
#include <scsi/osd_sec.h>
#include "osd_debug.h"
#ifndef TYPE_OSD
# define TYPE_OSD 0x11
#endif
#ifndef SCSI_OSD_MAJOR
# define SCSI_OSD_MAJOR 260
#endif
#define SCSI_OSD_MAX_MINOR MINORMASK
static const char osd_name[] = "osd";
static const char *osd_version_string = "open-osd 0.2.1";
MODULE_AUTHOR("Boaz Harrosh <ooo@electrozaur.com>");
MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_OSD_MAJOR);
MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
struct osd_uld_device {
int minor;
struct device class_dev;
struct cdev cdev;
struct osd_dev od;
struct osd_dev_info odi;
struct gendisk *disk;
};
struct osd_dev_handle {
struct osd_dev od;
struct file *file;
struct osd_uld_device *oud;
} ;
static DEFINE_IDA(osd_minor_ida);
/*
* scsi sysfs attribute operations
*/
static ssize_t osdname_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
class_dev);
return sprintf(buf, "%s\n", ould->odi.osdname);
}
static DEVICE_ATTR_RO(osdname);
static ssize_t systemid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct osd_uld_device *ould = container_of(dev, struct osd_uld_device,
class_dev);
memcpy(buf, ould->odi.systemid, ould->odi.systemid_len);
return ould->odi.systemid_len;
}
static DEVICE_ATTR_RO(systemid);
static struct attribute *osd_uld_attrs[] = {
&dev_attr_osdname.attr,
&dev_attr_systemid.attr,
NULL,
};
ATTRIBUTE_GROUPS(osd_uld);
static struct class osd_uld_class = {
.owner = THIS_MODULE,
.name = "scsi_osd",
.dev_groups = osd_uld_groups,
};
/*
* Char Device operations
*/
static int osd_uld_open(struct inode *inode, struct file *file)
{
struct osd_uld_device *oud = container_of(inode->i_cdev,
struct osd_uld_device, cdev);
get_device(&oud->class_dev);
/* cache osd_uld_device on file handle */
file->private_data = oud;
OSD_DEBUG("osd_uld_open %p\n", oud);
return 0;
}
static int osd_uld_release(struct inode *inode, struct file *file)
{
struct osd_uld_device *oud = file->private_data;
OSD_DEBUG("osd_uld_release %p\n", file->private_data);
file->private_data = NULL;
put_device(&oud->class_dev);
return 0;
}
/* FIXME: Only one vector for now */
unsigned g_test_ioctl;
do_test_fn *g_do_test;
int osduld_register_test(unsigned ioctl, do_test_fn *do_test)
{
if (g_test_ioctl)
return -EINVAL;
g_test_ioctl = ioctl;
g_do_test = do_test;
return 0;
}
EXPORT_SYMBOL(osduld_register_test);
void osduld_unregister_test(unsigned ioctl)
{
if (ioctl == g_test_ioctl) {
g_test_ioctl = 0;
g_do_test = NULL;
}
}
EXPORT_SYMBOL(osduld_unregister_test);
static do_test_fn *_find_ioctl(unsigned cmd)
{
if (g_test_ioctl == cmd)
return g_do_test;
else
return NULL;
}
static long osd_uld_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct osd_uld_device *oud = file->private_data;
int ret;
do_test_fn *do_test;
do_test = _find_ioctl(cmd);
if (do_test)
ret = do_test(&oud->od, cmd, arg);
else {
OSD_ERR("Unknown ioctl %d: osd_uld_device=%p\n", cmd, oud);
ret = -ENOIOCTLCMD;
}
return ret;
}
static const struct file_operations osd_fops = {
.owner = THIS_MODULE,
.open = osd_uld_open,
.release = osd_uld_release,
.unlocked_ioctl = osd_uld_ioctl,
.llseek = noop_llseek,
};
struct osd_dev *osduld_path_lookup(const char *name)
{
struct osd_uld_device *oud;
struct osd_dev_handle *odh;
struct file *file;
int error;
if (!name || !*name) {
OSD_ERR("Mount with !path || !*path\n");
return ERR_PTR(-EINVAL);
}
odh = kzalloc(sizeof(*odh), GFP_KERNEL);
if (unlikely(!odh))
return ERR_PTR(-ENOMEM);
file = filp_open(name, O_RDWR, 0);
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto free_od;
}
if (file->f_op != &osd_fops){
error = -EINVAL;
goto close_file;
}
oud = file->private_data;
odh->od = oud->od;
odh->file = file;
odh->oud = oud;
return &odh->od;
close_file:
fput(file);
free_od:
kfree(odh);
return ERR_PTR(error);
}
EXPORT_SYMBOL(osduld_path_lookup);
static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
const u8 *a2, unsigned a2_len)
{
if (!a2_len) /* User string is Empty means don't care */
return true;
if (a1_len != a2_len)
return false;
return 0 == memcmp(a1, a2, a1_len);
}
static int _match_odi(struct device *dev, const void *find_data)
{
struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
class_dev);
const struct osd_dev_info *odi = find_data;
if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
odi->systemid, odi->systemid_len) &&
_the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
odi->osdname, odi->osdname_len)) {
OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
odi->systemid_len, odi->osdname_len);
return 1;
} else {
return 0;
}
}
/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
*
* if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
* care. .e.g if they're both zero /dev/osd0 is returned.
*/
struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
{
struct device *dev = class_find_device(&osd_uld_class, NULL, odi, _match_odi);
if (likely(dev)) {
struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
struct osd_uld_device *oud = container_of(dev,
struct osd_uld_device, class_dev);
if (unlikely(!odh)) {
put_device(dev);
return ERR_PTR(-ENOMEM);
}
odh->od = oud->od;
odh->oud = oud;
return &odh->od;
}
return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL(osduld_info_lookup);
void osduld_put_device(struct osd_dev *od)
{
if (od && !IS_ERR(od)) {
struct osd_dev_handle *odh =
container_of(od, struct osd_dev_handle, od);
struct osd_uld_device *oud = odh->oud;
BUG_ON(od->scsi_device != oud->od.scsi_device);
/* If scsi has released the device (logout), and exofs has last
* reference on oud it will be freed by above osd_uld_release
* within fput below. But this will oops in cdev_release which
* is called after the fops->release. A get_/put_ pair makes
* sure we have a cdev for the duration of fput
*/
if (odh->file) {
get_device(&oud->class_dev);
fput(odh->file);
}
put_device(&oud->class_dev);
kfree(odh);
}
}
EXPORT_SYMBOL(osduld_put_device);
const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
{
struct osd_dev_handle *odh =
container_of(od, struct osd_dev_handle, od);
return &odh->oud->odi;
}
EXPORT_SYMBOL(osduld_device_info);
bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
{
struct osd_dev_handle *odh =
container_of(od, struct osd_dev_handle, od);
struct osd_uld_device *oud = odh->oud;
return (oud->odi.systemid_len == odi->systemid_len) &&
_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
odi->systemid, odi->systemid_len) &&
(oud->odi.osdname_len == odi->osdname_len) &&
_the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
odi->osdname, odi->osdname_len);
}
EXPORT_SYMBOL(osduld_device_same);
/*
* Scsi Device operations
*/
static int __detect_osd(struct osd_uld_device *oud)
{
struct scsi_device *scsi_device = oud->od.scsi_device;
struct scsi_sense_hdr sense_hdr;
char caps[OSD_CAP_LEN];
int error;
/* sending a test_unit_ready as first command seems to be needed
* by some targets
*/
OSD_DEBUG("start scsi_test_unit_ready %p %p %p\n",
oud, scsi_device, scsi_device->request_queue);
error = scsi_test_unit_ready(scsi_device, 10*HZ, 5, &sense_hdr);
if (error)
OSD_ERR("warning: scsi_test_unit_ready failed\n");
osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
return -ENODEV;
return 0;
}
static void __remove(struct device *dev)
{
struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
class_dev);
struct scsi_device *scsi_device = oud->od.scsi_device;
kfree(oud->odi.osdname);
osd_dev_fini(&oud->od);
scsi_device_put(scsi_device);
OSD_INFO("osd_remove %s\n",
oud->disk ? oud->disk->disk_name : NULL);
if (oud->disk)
put_disk(oud->disk);
kfree(oud);
}
static int osd_probe(struct device *dev)
{
struct scsi_device *scsi_device = to_scsi_device(dev);
struct gendisk *disk;
struct osd_uld_device *oud;
int minor;
int error;
if (scsi_device->type != TYPE_OSD)
return -ENODEV;
minor = ida_alloc_max(&osd_minor_ida, SCSI_OSD_MAX_MINOR, GFP_KERNEL);
if (minor == -ENOSPC)
return -EBUSY;
if (minor < 0)
return -ENODEV;
error = -ENOMEM;
oud = kzalloc(sizeof(*oud), GFP_KERNEL);
if (NULL == oud)
goto err_retract_minor;
/* class device member */
device_initialize(&oud->class_dev);
dev_set_drvdata(dev, oud);
oud->minor = minor;
oud->class_dev.devt = MKDEV(SCSI_OSD_MAJOR, oud->minor);
oud->class_dev.class = &osd_uld_class;
oud->class_dev.parent = dev;
oud->class_dev.release = __remove;
/* hold one more reference to the scsi_device that will get released
* in __release, in case a logout is happening while fs is mounted
*/
if (scsi_device_get(scsi_device))
goto err_retract_minor;
osd_dev_init(&oud->od, scsi_device);
/* allocate a disk and set it up */
/* FIXME: do we need this since sg has already done that */
disk = alloc_disk(1);
if (!disk) {
OSD_ERR("alloc_disk failed\n");
goto err_free_osd;
}
disk->major = SCSI_OSD_MAJOR;
disk->first_minor = oud->minor;
sprintf(disk->disk_name, "osd%d", oud->minor);
oud->disk = disk;
/* Detect the OSD Version */
error = __detect_osd(oud);
if (error) {
OSD_ERR("osd detection failed, non-compatible OSD device\n");
goto err_free_osd;
}
/* init the char-device for communication with user-mode */
cdev_init(&oud->cdev, &osd_fops);
oud->cdev.owner = THIS_MODULE;
error = dev_set_name(&oud->class_dev, "%s", disk->disk_name);
if (error) {
OSD_ERR("dev_set_name failed => %d\n", error);
goto err_free_osd;
}
error = cdev_device_add(&oud->cdev, &oud->class_dev);
if (error) {
OSD_ERR("device_register failed => %d\n", error);
goto err_free_osd;
}
OSD_INFO("osd_probe %s\n", disk->disk_name);
return 0;
err_free_osd:
put_device(&oud->class_dev);
err_retract_minor:
ida_free(&osd_minor_ida, minor);
return error;
}
static int osd_remove(struct device *dev)
{
struct scsi_device *scsi_device = to_scsi_device(dev);
struct osd_uld_device *oud = dev_get_drvdata(dev);
if (oud->od.scsi_device != scsi_device) {
OSD_ERR("Half cooked osd-device %p, || %p!=%p",
dev, oud->od.scsi_device, scsi_device);
}
cdev_device_del(&oud->cdev, &oud->class_dev);
ida_free(&osd_minor_ida, oud->minor);
put_device(&oud->class_dev);
return 0;
}
/*
* Global driver and scsi registration
*/
static struct scsi_driver osd_driver = {
.gendrv = {
.name = osd_name,
.owner = THIS_MODULE,
.probe = osd_probe,
.remove = osd_remove,
}
};
static int __init osd_uld_init(void)
{
int err;
err = class_register(&osd_uld_class);
if (err) {
OSD_ERR("Unable to register sysfs class => %d\n", err);
return err;
}
err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
SCSI_OSD_MAX_MINOR, osd_name);
if (err) {
OSD_ERR("Unable to register major %d for osd ULD => %d\n",
SCSI_OSD_MAJOR, err);
goto err_out;
}
err = scsi_register_driver(&osd_driver.gendrv);
if (err) {
OSD_ERR("scsi_register_driver failed => %d\n", err);
goto err_out_chrdev;
}
OSD_INFO("LOADED %s\n", osd_version_string);
return 0;
err_out_chrdev:
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
err_out:
class_unregister(&osd_uld_class);
return err;
}
static void __exit osd_uld_exit(void)
{
scsi_unregister_driver(&osd_driver.gendrv);
unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
class_unregister(&osd_uld_class);
OSD_INFO("UNLOADED %s\n", osd_version_string);
}
module_init(osd_uld_init);
module_exit(osd_uld_exit);
/*
* osd_initiator.h - OSD initiator API definition
*
* Copyright (C) 2008 Panasas Inc. All rights reserved.
*
* Authors:
* Boaz Harrosh <ooo@electrozaur.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 <scsi/osd_protocol.h>
#include <scsi/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;
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;
blk_status_t 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.
*
* Allocate osd_request and initialize all members to the
* default/initial state.
*/
struct osd_request *osd_start_request(struct osd_dev *od);
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 - Receives 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 received 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__ */
/*
* Copyright (C) 2011
* Boaz Harrosh <ooo@electrozaur.com>
*
* Public Declarations of the ORE API
*
* This file is part of the ORE (Object Raid Engine) library.
*
* ORE is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. (GPL v2)
*
* ORE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the ORE; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __ORE_H__
#define __ORE_H__
#include <scsi/osd_initiator.h>
#include <scsi/osd_attributes.h>
#include <scsi/osd_sec.h>
#include <linux/pnfs_osd_xdr.h>
#include <linux/bug.h>
struct ore_comp {
struct osd_obj_id obj;
u8 cred[OSD_CAP_LEN];
};
struct ore_layout {
/* Our way of looking at the data_map */
enum pnfs_osd_raid_algorithm4
raid_algorithm;
unsigned stripe_unit;
unsigned mirrors_p1;
unsigned group_width;
unsigned parity;
u64 group_depth;
unsigned group_count;
/* Cached often needed calculations filled in by
* ore_verify_layout
*/
unsigned long max_io_length; /* Max length that should be passed to
* ore_get_rw_state
*/
};
struct ore_dev {
struct osd_dev *od;
};
struct ore_components {
unsigned first_dev; /* First logical device no */
unsigned numdevs; /* Num of devices in array */
/* If @single_comp == EC_SINGLE_COMP, @comps points to a single
* component. else there are @numdevs components
*/
enum EC_COMP_USAGE {
EC_SINGLE_COMP = 0, EC_MULTPLE_COMPS = 0xffffffff
} single_comp;
struct ore_comp *comps;
/* Array of pointers to ore_dev-* . User will usually have these pointed
* too a bigger struct which contain an "ore_dev ored" member and use
* container_of(oc->ods[i], struct foo_dev, ored) to access the bigger
* structure.
*/
struct ore_dev **ods;
};
/* ore_comp_dev Recievies a logical device index */
static inline struct osd_dev *ore_comp_dev(
const struct ore_components *oc, unsigned i)
{
BUG_ON((i < oc->first_dev) || (oc->first_dev + oc->numdevs <= i));
return oc->ods[i - oc->first_dev]->od;
}
static inline void ore_comp_set_dev(
struct ore_components *oc, unsigned i, struct osd_dev *od)
{
oc->ods[i - oc->first_dev]->od = od;
}
struct ore_striping_info {
u64 offset;
u64 obj_offset;
u64 length;
u64 first_stripe_start; /* only used in raid writes */
u64 M; /* for truncate */
unsigned bytes_in_stripe;
unsigned dev;
unsigned par_dev;
unsigned unit_off;
unsigned cur_pg;
unsigned cur_comp;
unsigned maxdevUnits;
};
struct ore_io_state;
typedef void (*ore_io_done_fn)(struct ore_io_state *ios, void *private);
struct _ore_r4w_op {
/* @Priv given here is passed ios->private */
struct page * (*get_page)(void *priv, u64 page_index, bool *uptodate);
void (*put_page)(void *priv, struct page *page);
};
struct ore_io_state {
struct kref kref;
struct ore_striping_info si;
void *private;
ore_io_done_fn done;
struct ore_layout *layout;
struct ore_components *oc;
/* Global read/write IO*/
loff_t offset;
unsigned long length;
void *kern_buff;
struct page **pages;
unsigned nr_pages;
unsigned pgbase;
unsigned pages_consumed;
/* Attributes */
unsigned in_attr_len;
struct osd_attr *in_attr;
unsigned out_attr_len;
struct osd_attr *out_attr;
bool reading;
/* House keeping of Parity pages */
bool extra_part_alloc;
struct page **parity_pages;
unsigned max_par_pages;
unsigned cur_par_page;
unsigned sgs_per_dev;
struct __stripe_pages_2d *sp2d;
struct ore_io_state *ios_read_4_write;
const struct _ore_r4w_op *r4w;
/* Variable array of size numdevs */
unsigned numdevs;
struct ore_per_dev_state {
struct osd_request *or;
struct bio *bio;
loff_t offset;
unsigned length;
unsigned last_sgs_total;
unsigned dev;
struct osd_sg_entry *sglist;
unsigned cur_sg;
} per_dev[];
};
static inline unsigned ore_io_state_size(unsigned numdevs)
{
return sizeof(struct ore_io_state) +
sizeof(struct ore_per_dev_state) * numdevs;
}
/* ore.c */
int ore_verify_layout(unsigned total_comps, struct ore_layout *layout);
void ore_calc_stripe_info(struct ore_layout *layout, u64 file_offset,
u64 length, struct ore_striping_info *si);
int ore_get_rw_state(struct ore_layout *layout, struct ore_components *comps,
bool is_reading, u64 offset, u64 length,
struct ore_io_state **ios);
int ore_get_io_state(struct ore_layout *layout, struct ore_components *comps,
struct ore_io_state **ios);
void ore_put_io_state(struct ore_io_state *ios);
typedef void (*ore_on_dev_error)(struct ore_io_state *ios, struct ore_dev *od,
unsigned dev_index, enum osd_err_priority oep,
u64 dev_offset, u64 dev_len);
int ore_check_io(struct ore_io_state *ios, ore_on_dev_error rep);
int ore_create(struct ore_io_state *ios);
int ore_remove(struct ore_io_state *ios);
int ore_write(struct ore_io_state *ios);
int ore_read(struct ore_io_state *ios);
int ore_truncate(struct ore_layout *layout, struct ore_components *comps,
u64 size);
int extract_attr_from_ios(struct ore_io_state *ios, struct osd_attr *attr);
extern const struct osd_attr g_attr_logical_length;
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment