diff options
Diffstat (limited to 'usr/src')
21 files changed, 2854 insertions, 272 deletions
diff --git a/usr/src/cmd/iscsi/iscsitgtd/Makefile b/usr/src/cmd/iscsi/iscsitgtd/Makefile index bc62138311..f934118f45 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/Makefile +++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile @@ -31,7 +31,7 @@ OBJS = main.o mgmt.o mgmt_create.o mgmt_list.o mgmt_modify.o mgmt_remove.o OBJS += iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o OBJS += iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o OBJS += t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o -OBJS += util.o util_err.o util_ifname.o util_port.o util_queue.o +OBJS += t10_spc_pr.o util.o util_err.o util_ifname.o util_port.o util_queue.o OBJS += isns_client.o isns.o POFILE= iscsitgtd.po POFILES = $(OBJS:%.o=%.po) diff --git a/usr/src/cmd/iscsi/iscsitgtd/Makefile.com b/usr/src/cmd/iscsi/iscsitgtd/Makefile.com index 27a6d55fd7..2400aab467 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/Makefile.com +++ b/usr/src/cmd/iscsi/iscsitgtd/Makefile.com @@ -34,7 +34,7 @@ COBJS = main.o mgmt.o mgmt_create.o mgmt_list.o mgmt_modify.o mgmt_remove.o COBJS += iscsi_authclient.o iscsi_authglue.o iscsi_cmd.o iscsi_conn.o COBJS += iscsi_crc.o iscsi_ffp.o iscsi_login.o iscsi_sess.o radius.o COBJS += t10_sam.o t10_spc.o t10_sbc.o t10_raw_if.o t10_ssc.o t10_osd.o -COBJS += util.o util_err.o util_ifname.o util_port.o util_queue.o +COBJS += t10_spc_pr.o util.o util_err.o util_ifname.o util_port.o util_queue.o COBJS += isns_client.o isns.o OBJS= $(COBJS) $(DSRC:%.d=%.o) SRCS= $(COBJS:%.o=../%.c) $(COMMON_SRCS) diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c index 12d7538ec9..b2ca1b0d1d 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c +++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -71,8 +71,7 @@ session_init() Boolean_t session_alloc(iscsi_conn_t *c, uint8_t *isid) { - iscsi_sess_t *s, - *n; + iscsi_sess_t *s, *n; if (c->c_sess != NULL) return (True); @@ -94,7 +93,7 @@ session_alloc(iscsi_conn_t *c, uint8_t *isid) } (void) pthread_mutex_unlock(&sess_mutex); - bcopy(isid, s->s_isid, 6); + bcopy(isid, &s->s_isid, 6); (void) pthread_mutex_init(&s->s_mutex, NULL); c->c_sess = s; @@ -171,7 +170,7 @@ session_free(iscsi_sess_t *s) static Boolean_t session_remove_connection(iscsi_sess_t *s, iscsi_conn_t *c) { - bzero(s->s_isid, 6); + s->s_isid = 0; return (True); } @@ -187,8 +186,8 @@ Boolean_t convert_i_local(char *ip, char **rtn) { tgt_node_t *inode = NULL; - char *iname, - *name; + char *iname; + char *name; while ((inode = tgt_node_next(main_config, XML_ELEMENT_INIT, inode)) != NULL) { @@ -298,8 +297,8 @@ sess_process(void *v) mgmt_request_t *mgmt; name_request_t *nr; t10_cmd_t *t10_cmd; - char **buf, - local_buf[16]; + char **buf; + char local_buf[16]; int lun; extern void dataout_callback(t10_cmd_t *t, char *data, size_t *xfer); @@ -314,6 +313,12 @@ sess_process(void *v) if (s->s_t10 == NULL) { /* + * Persistent Reservervation Support requires + * access to both the transportID and iSCSI + * Session ID + */ + + /* * The value of 0x960 comes from T10. * See SPC-4, revision 1a, section 6.4.2, * table 87 @@ -323,9 +328,12 @@ sess_process(void *v) */ s->s_t10 = t10_handle_create(s->s_t_name, T10_TRANS_ISCSI, s->s_conn_head->c_tpgt, + s->s_conn_head->c_sess->s_i_name, + s->s_conn_head->c_sess->s_isid, s->s_conn_head->c_max_burst_len, s->s_t10q, dataout_callback); } + if (t10_cmd_create(s->s_t10, cmd->c_lun, cmd->c_scb, cmd->c_scb_len, (transport_t)cmd, &t10_cmd) == False) { @@ -553,12 +561,8 @@ session_validate(iscsi_sess_t *s) { iscsi_sess_t *check; - queue_prt(s->s_mgmtq, Q_SESS_NONIO, - "SES%x %s ISID[%02x%02x%02x%02x%02x%02x]\n", - s->s_num, s->s_i_alias == NULL ? s->s_i_name : s->s_i_alias, - s->s_isid[0], s->s_isid[1], s->s_isid[2], - s->s_isid[3], s->s_isid[4], s->s_isid[5]); - + queue_prt(s->s_mgmtq, Q_SESS_NONIO, "SES%x %s ISID[%016x]\n", s->s_num, + s->s_i_alias == NULL ? s->s_i_name : s->s_i_alias, s->s_isid); /* * SessionType=Discovery which means no target name and therefore @@ -590,7 +594,7 @@ session_validate(iscsi_sess_t *s) * reinstating a new iSCSI session in its place (with the * same ISID). */ - if (bcmp(check->s_isid, s->s_isid, 6) == 0) { + if (check->s_isid == s->s_isid) { queue_prt(s->s_mgmtq, Q_SESS_NONIO, "SES%x Implicit shutdown\n", check->s_num); if (check->s_conn_head->c_state == S5_LOGGED_IN) @@ -681,7 +685,7 @@ sess_set_auth(iscsi_sess_t *isp) } if (iscsiAuthClientSetVersion(auth_client, - iscsiAuthVersionRfc) != iscsiAuthStatusNoError) { + iscsiAuthVersionRfc) != iscsiAuthStatusNoError) { syslog(LOG_ERR, "iscsi connection login failed - " "unable to set version\n"); return; @@ -716,8 +720,7 @@ sess_set_auth(iscsi_sess_t *isp) } if (iscsiAuthClientSetAuthRemote(auth_client, - isp->sess_auth.auth_enabled) != - iscsiAuthStatusNoError) { + isp->sess_auth.auth_enabled) != iscsiAuthStatusNoError) { syslog(LOG_ERR, "iscsi connection login failed - " "unable to set remote authentication\n"); return; diff --git a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h index 4426046117..c0703f9dd4 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h +++ b/usr/src/cmd/iscsi/iscsitgtd/iscsi_sess.h @@ -20,12 +20,12 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _SESSION_H -#define _SESSION_H +#ifndef _ISCSI_SESS_H +#define _ISCSI_SESS_H #pragma ident "%Z%%M% %I% %E% SMI" @@ -92,7 +92,8 @@ typedef struct iscsi_sess { char *s_i_name, *s_i_alias, *s_t_name; - uint8_t s_isid[6]; + uint64_t s_isid; + /* * This is the highest packet number we've seen and is * used during replies. @@ -139,4 +140,4 @@ Boolean_t session_validate(struct iscsi_sess *s); } #endif -#endif /* _SESSION_H */ +#endif /* _ISCSI_SESS_H */ diff --git a/usr/src/cmd/iscsi/iscsitgtd/main.c b/usr/src/cmd/iscsi/iscsitgtd/main.c index 4ac843dba2..b19f856e25 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/main.c +++ b/usr/src/cmd/iscsi/iscsitgtd/main.c @@ -89,7 +89,8 @@ tgt_node_t *main_config, Boolean_t enforce_strict_guid = True, thin_provisioning = False, disable_tpgs = False, - dbg_timestamps = False; + dbg_timestamps = False, + pgr_persist = True; int targets_vers_maj, targets_vers_min, main_vers_maj, @@ -370,6 +371,8 @@ process_config(char *file) &disable_tpgs); (void) tgt_find_value_boolean(node, XML_ELEMENT_TIMESTAMPS, &dbg_timestamps); + (void) tgt_find_value_boolean(node, XML_ELEMENT_PGR_PERSIST, + &pgr_persist); if (tgt_find_value_int(node, XML_ELEMENT_LOGLVL, &qlog_lvl) == True) queue_log(True); diff --git a/usr/src/cmd/iscsi/iscsitgtd/queue.h b/usr/src/cmd/iscsi/iscsitgtd/queue.h index 5813187026..c4fad54edd 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/queue.h +++ b/usr/src/cmd/iscsi/iscsitgtd/queue.h @@ -41,29 +41,39 @@ extern "C" { #include <iscsitgt_impl.h> -#define Q_CONN_ERRS 0x00001 -#define Q_CONN_LOGIN 0x00002 -#define Q_CONN_NONIO 0x00004 -#define Q_CONN_IO 0x00008 - -#define Q_SESS_ERRS 0x00010 -#define Q_SESS_LOGIN 0x00020 -#define Q_SESS_NONIO 0x00040 -#define Q_SESS_IO 0x00080 - -#define Q_STE_ERRS 0x00100 -#define Q_STE_NONIO 0x00200 -#define Q_STE_IO 0x00400 - -#define Q_GEN_ERRS 0x01000 -#define Q_GEN_DETAILS 0x02000 - -#define Q_ISNS_DBG 0x10000 +/* Connections */ +#define Q_CONN_ERRS 0x00000001 +#define Q_CONN_LOGIN 0x00000002 +#define Q_CONN_NONIO 0x00000004 +#define Q_CONN_IO 0x00000008 + +/* Sessions */ +#define Q_SESS_ERRS 0x00000010 +#define Q_SESS_LOGIN 0x00000020 +#define Q_SESS_NONIO 0x00000040 +#define Q_SESS_IO 0x00000080 + +/* SCSI Target Emulation */ +#define Q_STE_ERRS 0x00000100 +#define Q_STE_NONIO 0x00000200 +#define Q_STE_IO 0x00000400 + +/* General Errors */ +#define Q_GEN_ERRS 0x00001000 +#define Q_GEN_DETAILS 0x00002000 + +/* ISCSI Debugging */ +#define Q_ISNS_DBG 0x00004000 + +/* Persistent Reservations */ +#define Q_PR_ERRS 0x00010000 +#define Q_PR_NONIO 0x00020000 +#define Q_PR_IO 0x00040000 /* * When used the queue request will be place at the head of the queue. */ -#define Q_HIGH 0x10000 +#define Q_HIGH 0x80000000 extern int qlog_lvl; diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10.h b/usr/src/cmd/iscsi/iscsitgtd/t10.h index 502304dccb..3160e7bc0d 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10.h +++ b/usr/src/cmd/iscsi/iscsitgtd/t10.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -126,6 +126,8 @@ typedef enum { #define T10_CMD_RESID(cmd) (cmd->c_resid) #define T10_SENSE_LEN(cmd) (cmd->c_cmd_sense_len) #define T10_SENSE_DATA(cmd) (cmd->c_cmd_sense) +#define T10_PGR_TID(cmd) (cmd->c_lu->l_targ->s_transportID) +#define T10_PGR_ISID(cmd) (cmd->c_lu->l_targ->s_isid) #define T10_DEFAULT_TPG 1 @@ -341,7 +343,7 @@ typedef struct t10_lu_common { Boolean_t l_fast_write_ack; /* - * AVL tree containing all I_T_Q nexus' which are actively using + * AVL tree containing all I_T_L nexus' which are actively using * this LUN. */ avl_tree_t l_all_open; @@ -427,6 +429,7 @@ typedef struct t10_lu_impl { int l_targ_lun; Boolean_t l_dsense_enabled; + Boolean_t l_pgr_read; /* * Statistics on a per ITL basis @@ -463,7 +466,14 @@ typedef struct t10_targ_impl { /* * Target Port Set */ - int s_tp_grp; + int s_tpgt; + + /* + * PERSISTENT RESERVATION support, the transportID & ISID + * SPC-3 revision 23, Section 7.5.4.5, Table 290 + */ + char *s_transportID; + uint64_t s_isid; /* * transport version number to use in standard inquiry data @@ -528,8 +538,9 @@ void lu_buserr_handler(int sig, siginfo_t *sip, void *v); * t10_handle_create -- create target handle to be used by transports */ t10_targ_handle_t -t10_handle_create(char *targ_name, int trans_version, int tpg, int max_out, - target_queue_t *transq, void (*datain_cb)(t10_cmd_t *, char *, size_t *)); +t10_handle_create(char *targ, int trans_vers, int tpg, char *tid, uint64_t isid, + int max_out, target_queue_t *tq, + void (*datain_cb)(t10_cmd_t *, char *, size_t *)); /* * t10_handle_disable -- drains commands from emulation queues diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c b/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c index 24d4bb722f..03f8422704 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_raw_if.c @@ -234,8 +234,8 @@ raw_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, static void raw_read_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - size_t req_len, - xfer; + size_t req_len; + size_t xfer; off_t offset = 0; raw_io_t *io; Boolean_t last; @@ -285,8 +285,8 @@ raw_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; off_t offset = 0; - uint32_t cnt, - min; + uint32_t cnt; + uint32_t min; raw_io_t *io; uint64_t err_blkno; int sense_len; @@ -499,8 +499,8 @@ raw_read_cmplt(emul_handle_t id) static void raw_write_tape(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - size_t request_len, - xfer; + size_t request_len; + size_t xfer; raw_io_t *io; request_len = (cdb[2] << 16) | (cdb[3] << 8) | cdb[4]; @@ -824,6 +824,37 @@ raw_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) } static void +raw_persist_in(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) +{ + raw_io_t *io; + + if ((io = do_datain(cmd, cdb, CDB_GROUP1, 0)) == NULL) { + trans_send_complete(cmd, STATUS_CHECK); + } else { + trans_send_complete(cmd, io->r_status); + raw_free_io(io); + } +} + +static void +raw_persist_out(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) +{ + size_t len; + + len = (cdb[5] << 24) | (cdb[6] << 16) | (cdb[7] << 8) | cdb[8]; + do_dataout(cmd, cdb, cdb_len, len); +} + +/*ARGSUSED*/ +static void +raw_persist_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, + size_t data_len) +{ + raw_io_t *io = (raw_io_t *)id; + trans_send_complete(cmd, do_uscsi(cmd, io, RawDataToDevice)); +} + +static void raw_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { raw_io_t *io; @@ -1351,8 +1382,8 @@ static scsi_cmd_table_t raw_table[] = { { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, - { spc_unsupported, NULL, NULL, NULL }, - { spc_unsupported, NULL, NULL, NULL }, + { raw_persist_in, NULL, NULL, "PERSISTENT_RESERVE_IN" }, + { raw_persist_out, raw_persist_data, NULL, "PERSISTENT_RESERVE_OUT" }, /* 0x60 -- 0x6f */ { spc_unsupported, NULL, NULL, NULL }, diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c b/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c index cebde4af45..582ab51f84 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sam.c @@ -99,7 +99,7 @@ static int find_cmd_by_addr(const void *v1, const void *v2); static sam_device_table_t sam_emul_table[]; /* - * Global variables + * Local variables */ static avl_tree_t lu_list; static pthread_mutex_t lu_list_mutex; @@ -205,8 +205,9 @@ t10_aio_done(void *v) * []---- */ t10_targ_handle_t -t10_handle_create(char *targ, int trans_version, int tpg, int max_out, - target_queue_t *tq, void (*datain_cb)(t10_cmd_t *, char *, size_t *)) +t10_handle_create(char *targ, int trans_vers, int tpg, char *tid, uint64_t isid, + int max_out, target_queue_t *tq, + void (*datain_cb)(t10_cmd_t *, char *, size_t *)) { t10_targ_impl_t *t = calloc(1, sizeof (t10_targ_impl_t)); @@ -217,12 +218,19 @@ t10_handle_create(char *targ, int trans_version, int tpg, int max_out, t->s_targ_num = t10_num++; (void) pthread_mutex_unlock(&t10_mutex); t->s_targ_base = strdup(targ); - t->s_trans_vers = trans_version; + t->s_trans_vers = trans_vers; t->s_maxout = max_out; t->s_to_transport = tq; t->s_dataout_cb = datain_cb; /* + * Persistent Reservervation Support requires access to both + * the transportID and iSCSI Session ID + */ + t->s_transportID = strdup(tid); + t->s_isid = isid; + + /* * Once we actually support two or more transports it would be * possible for a collision between the underlying transports * target port group values since one wouldn't necessarily know @@ -234,13 +242,13 @@ t10_handle_create(char *targ, int trans_version, int tpg, int max_out, * to determine relative path numbering there's no issue with changing * this later if need be. */ - switch (trans_version) { + switch (trans_vers) { case T10_TRANS_ISCSI: - t->s_tp_grp = 0x0000 | tpg; + t->s_tpgt = 0x0000 | tpg; break; case T10_TRANS_FC: - t->s_tp_grp = 0x8000 | tpg; + t->s_tpgt = 0x8000 | tpg; break; } @@ -286,8 +294,8 @@ t10_handle_destroy(t10_targ_handle_t tp) { t10_targ_impl_t *t = (t10_targ_impl_t *)tp; t10_lu_impl_t *l; - t10_cmd_t *c, - *c2free; + t10_cmd_t *c; + t10_cmd_t *c2free; int fast_free = 0; (void) pthread_mutex_lock(&t->s_mutex); @@ -348,6 +356,7 @@ t10_handle_destroy(t10_targ_handle_t tp) (void) pthread_mutex_unlock(&t->s_mutex); free(t->s_targ_base); + free(t->s_transportID); free(t); } @@ -689,8 +698,8 @@ Boolean_t t10_task_mgmt(t10_targ_handle_t t1, TaskOp_t op, int opt_lun, void *tag) { t10_targ_impl_t *t = (t10_targ_impl_t *)t1; - t10_lu_impl_t search, - *lu; + t10_lu_impl_t search; + t10_lu_impl_t *lu; switch (op) { case InventoryChange: @@ -753,8 +762,8 @@ t10_targ_stat(t10_targ_handle_t t1, char **buf) { t10_targ_impl_t *t = (t10_targ_impl_t *)t1; t10_lu_impl_t *itl; - char lb[32], - *p; + char lb[32]; + char *p; /* * It's possible for the management interfaces to request stats @@ -819,8 +828,8 @@ t10_thick_provision(char *target, int lun, target_queue_t *q) t10_cmd_t *cmd = NULL; uint8_t cdb[16]; /* ---- fake buffer ---- */ diskaddr_t offset = 0; - size_t size, - sync_size; + size_t size; + size_t sync_size; msg_t *m = NULL; target_queue_t *rq = NULL; char path[MAXPATHLEN]; @@ -835,7 +844,7 @@ t10_thick_provision(char *target, int lun, target_queue_t *q) * having something fixed/change in one location that isn't * in another. Obvious right? */ - if ((t = t10_handle_create(target, 0, 0, 0, q, NULL)) == NULL) { + if ((t = t10_handle_create(target, 0, 0, "", 0, 0, q, NULL)) == NULL) { queue_prt(mgmtq, Q_STE_ERRS, "STE%x Failed to create handle\n", lun); return (False); @@ -1311,19 +1320,19 @@ trans_params_area(t10_cmd_t *cmd) static Boolean_t t10_find_lun(t10_targ_impl_t *t, int lun, t10_cmd_t *cmd) { - t10_lu_impl_t *l = NULL, - search; - avl_index_t wc = 0, /* where common */ - wt = 0; /* where target */ - char *guid = NULL, - *str, - *dataset = NULL; - t10_lu_common_t lc, - *common = NULL; - tgt_node_t *n = NULL, - *n1, - *targ, - *ll; + t10_lu_impl_t *l = NULL; + t10_lu_impl_t search; + avl_index_t wc = 0; /* where common */ + avl_index_t wt = 0; /* where target */ + char *guid = NULL; + char *str; + char *dataset = NULL; + t10_lu_common_t lc; + t10_lu_common_t *common = NULL; + tgt_node_t *n = NULL; + tgt_node_t *n1; + tgt_node_t *targ; + tgt_node_t *ll; xmlTextReaderPtr r = NULL; char path[MAXPATHLEN]; int xml_fd = -1; @@ -1702,11 +1711,11 @@ lu_runner(void *v) msg_t *m; t10_lu_impl_t *itl; t10_cmd_t *cmd; - char *data, - *path; - size_t data_len, - new_size, - offset; + char *data; + char *path; + size_t data_len; + size_t new_size; + size_t offset; ssize_t cc; void *provo_err; t10_shutdown_t *s; @@ -1736,8 +1745,8 @@ lu_runner(void *v) trans_send_complete(cmd, STATUS_CHECK); } else { lu->l_curr = cmd; - (*cmd->c_lu->l_cmd)(cmd, cmd->c_cdb, - cmd->c_cdb_len); + (*cmd->c_lu->l_cmd) + (cmd, cmd->c_cdb, cmd->c_cdb_len); lu->l_curr = NULL; } break; @@ -1896,13 +1905,13 @@ lu_runner(void *v) cmd->c_data, cmd->c_data_len); cmd->c_lu->l_cmds_read++; cmd->c_lu->l_sects_read += - cmd->c_data_len / 512; + cmd->c_data_len / 512; bcopy(cmd->c_data, (char *)lu->l_mmap + cmd->c_offset, cmd->c_data_len); cmd->c_lu->l_cmds_write++; cmd->c_lu->l_sects_write += - cmd->c_data_len / 512; + cmd->c_data_len / 512; lu->l_curr = NULL; lu->l_curr_provo = False; provo_err = 0; @@ -1920,7 +1929,7 @@ lu_runner(void *v) lu->l_num, errno); } provo_err = (cc == cmd->c_data_len) ? - (void *)0 : (void *)1; + (void *)0 : (void *)1; } /* * acknowledge this op and wait for next @@ -2119,13 +2128,13 @@ lu_remove_cmds(msg_t *m, void *v) static Boolean_t load_params(t10_lu_common_t *lu, char *basedir) { - char file[MAXPATHLEN], - *str; + char file[MAXPATHLEN]; + char *str; int oflags = O_RDWR|O_LARGEFILE|O_NDELAY; Boolean_t mmap_lun = True; tgt_node_t *node = NULL; - int version_maj = XML_VERS_LUN_MAJ, - version_min = XML_VERS_LUN_MIN; + int version_maj = XML_VERS_LUN_MAJ; + int version_min = XML_VERS_LUN_MIN; /* * Clean up from previous call to this function. This occurs if @@ -2398,8 +2407,8 @@ fallocate(int fd, off64_t len) static int find_lu_by_num(const void *v1, const void *v2) { - t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1, - *l2 = (t10_lu_impl_t *)v2; + t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1; + t10_lu_impl_t *l2 = (t10_lu_impl_t *)v2; if (l1->l_targ_lun < l2->l_targ_lun) return (-1); @@ -2416,8 +2425,8 @@ find_lu_by_num(const void *v1, const void *v2) static int find_lu_by_guid(const void *v1, const void *v2) { - t10_lu_common_t *l1 = (t10_lu_common_t *)v1, - *l2 = (t10_lu_common_t *)v2; + t10_lu_common_t *l1 = (t10_lu_common_t *)v1; + t10_lu_common_t *l2 = (t10_lu_common_t *)v2; int i; if (l1->l_guid_len != l2->l_guid_len) { @@ -2445,8 +2454,8 @@ find_lu_by_guid(const void *v1, const void *v2) static int find_lu_by_targ(const void *v1, const void *v2) { - t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1, - *l2 = (t10_lu_impl_t *)v2; + t10_lu_impl_t *l1 = (t10_lu_impl_t *)v1; + t10_lu_impl_t *l2 = (t10_lu_impl_t *)v2; if ((uint64_t)(uintptr_t)l1->l_targ < (uint64_t)(uintptr_t)l2->l_targ) return (-1); @@ -2465,8 +2474,8 @@ find_lu_by_targ(const void *v1, const void *v2) static int find_cmd_by_addr(const void *v1, const void *v2) { - uint64_t cmd1 = (uint64_t)(uintptr_t)v1, - cmd2 = (uint64_t)(uintptr_t)v2; + uint64_t cmd1 = (uint64_t)(uintptr_t)v1; + uint64_t cmd2 = (uint64_t)(uintptr_t)v2; if (cmd1 < cmd2) return (-1); diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c index 35014c7469..5fd90a561c 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.c @@ -49,10 +49,21 @@ #include "t10.h" #include "t10_spc.h" +#include "t10_spc_pr.h" #include "t10_sbc.h" #include "utility.h" /* + * External declarations + */ +Boolean_t spc_pr_read(t10_cmd_t *); +void spc_cmd_pr_in(t10_cmd_t *, uint8_t *, size_t); +void spc_cmd_pr_out(t10_cmd_t *, uint8_t *, size_t); +void spc_cmd_pr_out_data(t10_cmd_t *, emul_handle_t, size_t, char *, size_t); +void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len); +Boolean_t spc_pgr_check(t10_cmd_t *, uint8_t *); + +/* * Forward declarations */ static int sbc_mmap_overlap(const void *v1, const void *v2); @@ -60,7 +71,6 @@ static void sbc_overlap_store(disk_io_t *io); static void sbc_overlap_free(disk_io_t *io); static void sbc_overlap_check(disk_io_t *io); static void sbc_overlap_flush(disk_params_t *d); -static void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len); static void sbc_data(t10_cmd_t *cmd, emul_handle_t e, size_t offset, char *data, size_t data_len); static disk_io_t *sbc_io_alloc(t10_cmd_t *c); @@ -171,22 +181,6 @@ sbc_per_init(t10_lu_impl_t *itl) void sbc_per_fini(t10_lu_impl_t *itl) { - disk_params_t *d = (disk_params_t *)itl->l_common->l_dtype_params; - t10_lu_impl_t *lu; - - if (d->d_reserve_owner == itl) { - - /* - * Since we currently own the reservation, drop it, - * and restore everyone elses command pointer. - */ - lu = avl_first(&itl->l_common->l_all_open); - do { - lu->l_cmd = sbc_cmd; - lu = AVL_NEXT(&itl->l_common->l_all_open, lu); - } while (lu != NULL); - d->d_reserve_owner = NULL; - } } /* @@ -196,11 +190,19 @@ sbc_per_fini(t10_lu_impl_t *itl) * | This routine is called from within the SAM-3 Task router. * []---- */ -static void +void sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { scsi_cmd_table_t *e; + /* + * Determine if thre is persistent data for this I_T_L Nexus + */ + if (cmd->c_lu->l_pgr_read == False) { + spc_pr_read(cmd); + cmd->c_lu->l_pgr_read = True; + } + e = &cmd->c_lu->l_cmd_table[cdb[0]]; #ifdef FULL_DEBUG queue_prt(mgmtq, Q_STE_IO, "SBC%x LUN%d Cmd %s id=%p\n", @@ -215,34 +217,60 @@ sbc_cmd(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) * | sbc_cmd_reserve -- Run commands when another I_T_L has a reservation * []---- */ -static void +void sbc_cmd_reserved(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - scsi_cmd_table_t *e; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + Boolean_t conflict = False; + /* + * SPC-3, revision 23, Table 31 + * SPC commands that are allowed in the presence of various reservations + */ switch (cdb[0]) { - case SCMD_TEST_UNIT_READY: case SCMD_INQUIRY: - case SCMD_REPORT_LUNS: case SCMD_LOG_SENSE_G1: + case SCMD_PERSISTENT_RESERVE_IN: case SCMD_READ_MEDIA_SERIAL: + case SCMD_REPORT_LUNS: case SCMD_REPORT_TARGET_PORT_GROUPS: case SCMD_REQUEST_SENSE: - /* - * SPC-2, revision 20, Section 5.5.1 table 10 - * The specification allows these three commands - * to run even through there's a reservation in place. - */ - e = &cmd->c_lu->l_cmd_table[cdb[0]]; -#ifdef FULL_DEBUG - queue_prt(mgmtq, Q_STE_IO, "RESERVED: SBC%x LUN%d Cmd %s\n", - cmd->c_lu->l_targ->s_targ_num, cmd->c_lu->l_common->l_num, - e->cmd_name == NULL ? "(no name)" : e->cmd_name); -#endif - (*e->cmd_start)(cmd, cdb, cdb_len); + case SCMD_TEST_UNIT_READY: break; - default: + pthread_rwlock_rdlock(&res->res_rwlock); + switch (res->res_type) { + case RT_NONE: + /* conflict = False; */ + break; + case RT_PGR: + conflict = spc_pgr_check(cmd, cdb); + break; + default: + conflict = True; + break; + } + pthread_rwlock_unlock(&res->res_rwlock); + } + + queue_prt(mgmtq, Q_PR_IO, + "PGR%x LUN%d CDB:%s - sbc_cmd_reserved(%s:%s)\n", + cmd->c_lu->l_targ->s_targ_num, + cmd->c_lu->l_common->l_num, + cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL + ? "(no name)" + : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name, + res->res_type == RT_PGR ? "PGR" : + res->res_type == RT_NONE ? "" : "unknown", + conflict ? "Conflict" : "Allowed"); + + /* + * If no conflict at this point, allow command + */ + if (conflict == False) { + sbc_cmd(cmd, cdb, cdb_len); + } else { trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT); } } @@ -295,8 +323,8 @@ sbc_read(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; off_t offset = 0; - uint32_t cnt, - min; + uint32_t cnt; + uint32_t min; disk_io_t *io; void *mmap_data = T10_MMAP_AREA(cmd); uint64_t err_blkno; @@ -984,7 +1012,7 @@ sbc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) * to contain it's size. */ mode_hdr->length = sizeof (struct mode_header) - 1 + - MODE_BLK_DESC_LENGTH; + MODE_BLK_DESC_LENGTH; mode_hdr->bdesc_length = MODE_BLK_DESC_LENGTH; /* @@ -1057,7 +1085,7 @@ sbc_msense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) sizeof (struct mode_geometry); np = io->da_data + sizeof (*mode_hdr) + - mode_hdr->bdesc_length; + mode_hdr->bdesc_length; if (io->da_data_len < (sizeof (struct mode_format) + sizeof (struct mode_geometry) + sizeof (struct mode_cache_scsi3) + @@ -1207,8 +1235,8 @@ sbc_service_actiong4(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) static void sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - uint64_t capacity, - lba; + uint64_t capacity; + uint64_t lba; int rep_size; /* response data size */ struct scsi_capacity_16 *cap16; disk_params_t *d; @@ -1245,9 +1273,9 @@ sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) } lba = (uint64_t)cdb[2] << 56 | (uint64_t)cdb[3] << 48 | - (uint64_t)cdb[4] << 40 | (uint64_t)cdb[5] << 32 | - (uint64_t)cdb[6] << 24 | (uint64_t)cdb[7] << 16 | - (uint64_t)cdb[8] << 8 | (uint64_t)cdb[9]; + (uint64_t)cdb[4] << 40 | (uint64_t)cdb[5] << 32 | + (uint64_t)cdb[6] << 24 | (uint64_t)cdb[7] << 16 | + (uint64_t)cdb[8] << 8 | (uint64_t)cdb[9]; io = sbc_io_alloc(cmd); @@ -1286,57 +1314,12 @@ sbc_read_capacity16(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) /*ARGSUSED*/ static void -sbc_reserve(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) -{ - disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); - t10_lu_impl_t *lu; - - if (p == NULL) - return; - - if (cdb[1] & 0xe0 || SAM_CONTROL_BYTE_RESERVED(cdb[5])) { - spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); - spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); - trans_send_complete(cmd, STATUS_CHECK); - return; - } - - if ((p->d_reserve_owner != NULL) && - (p->d_reserve_owner != cmd->c_lu)) { - - trans_send_complete(cmd, STATUS_RESERVATION_CONFLICT); - return; - - } else if (p->d_reserve_owner == cmd->c_lu) { - - /* - * According SPC-2 revision 20, section 7.21.2 - * It shall be permissible for an initiator to - * reserve a logic unit that is currently reserved - * by that initiator - */ - trans_send_complete(cmd, STATUS_GOOD); - } else { - - lu = avl_first(&cmd->c_lu->l_common->l_all_open); - do { - if (lu != cmd->c_lu) - lu->l_cmd = sbc_cmd_reserved; - lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); - } while (lu != NULL); - p->d_reserve_owner = cmd->c_lu; - trans_send_complete(cmd, STATUS_GOOD); - } -} - -/*ARGSUSED*/ -static void sbc_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + disk_params_t *d = (disk_params_t *)T10_PARAMS_AREA(cmd); t10_lu_impl_t *lu; - if (p == NULL) + if (d == NULL) return; if (cdb[1] & 0xe0 || cdb[3] || cdb[4] || @@ -1347,7 +1330,7 @@ sbc_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) return; } - if (p->d_reserve_owner == NULL) { + if (d->d_sbc_reserve.res_type == RT_NONE) { /* * If nobody is the owner this command is successful. @@ -1365,7 +1348,11 @@ sbc_release(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) lu->l_cmd = sbc_cmd; lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); } while (lu != NULL); - p->d_reserve_owner = NULL; + + /* + * Remove reservation + */ + d->d_sbc_reserve.res_type == RT_NONE; trans_send_complete(cmd, STATUS_GOOD); } @@ -1376,10 +1363,10 @@ sbc_verify(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) /*LINTED*/ union scsi_cdb *u = (union scsi_cdb *)cdb; diskaddr_t addr; - uint32_t cnt, - chk_size; - uint64_t sz, - err_blkno; + uint32_t cnt; + uint32_t chk_size; + uint64_t sz; + uint64_t err_blkno; Boolean_t bytchk; char *chk_block; disk_io_t *io; @@ -1572,6 +1559,7 @@ sbc_verify_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, spc_sense_create(cmd, KEY_MISCOMPARE, 0); spc_sense_ascq(cmd, SPC_ASC_DATA_PATH, SPC_ASCQ_DATA_PATH); trans_send_complete(cmd, STATUS_CHECK); + free(on_disk_buf); sbc_io_free(io); return; } @@ -1579,6 +1567,7 @@ sbc_verify_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, spc_sense_create(cmd, KEY_MISCOMPARE, 0); spc_sense_ascq(cmd, SPC_ASC_MISCOMPARE, SPC_ASCQ_MISCOMPARE); trans_send_complete(cmd, STATUS_CHECK); + free(on_disk_buf); sbc_io_free(io); return; } @@ -1776,8 +1765,8 @@ sbc_io_free(emul_handle_t e) static int sbc_mmap_overlap(const void *v1, const void *v2) { - disk_io_t *d1 = (disk_io_t *)v1, - *d2 = (disk_io_t *)v2; + disk_io_t *d1 = (disk_io_t *)v1; + disk_io_t *d2 = (disk_io_t *)v2; if ((d1->da_data + d1->da_data_len) < d2->da_data) return (-1); @@ -1904,8 +1893,8 @@ static scsi_cmd_table_t lba_table[] = { { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, { spc_mselect, spc_mselect_data, NULL, "MODE_SELECT" }, - { sbc_reserve, NULL, NULL, "RESERVE" }, - { sbc_release, NULL, NULL, "RELEASE" }, + { spc_unsupported, NULL, NULL, NULL }, + { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, { sbc_msense, NULL, NULL, "MODE_SENSE" }, @@ -1984,8 +1973,8 @@ static scsi_cmd_table_t lba_table[] = { { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, { spc_unsupported, NULL, NULL, NULL }, - { spc_unsupported, NULL, NULL, "PERSISTENT_IN" }, - { spc_unsupported, NULL, NULL, "PERSISTENT_OUT" }, + { spc_cmd_pr_in, NULL, NULL, "PERSISTENT_RESERVE_IN" }, + { spc_cmd_pr_out, spc_cmd_pr_out_data, NULL, "PERSISTENT_RESERVE_OUT" }, /* 0x60 -- 0x6f */ { spc_unsupported, NULL, NULL, NULL }, diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h index 4e79c88f29..2a965b1159 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_sbc.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -120,8 +120,7 @@ typedef struct disk_params { Boolean_t d_fast_write; t10_lu_state_t d_state; - - t10_lu_impl_t *d_reserve_owner; + sbc_reserve_t d_sbc_reserve; } disk_params_t; typedef struct disk_io { diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c index e38cfd070d..9ffd1a8570 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.c @@ -51,7 +51,7 @@ #include "t10_spc.h" #include "target.h" -static void spc_free(emul_handle_t id); +void spc_free(emul_handle_t id); /* * []---- @@ -134,13 +134,13 @@ spc_request_sense(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) void spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { - uint8_t *rsp_buf, - *rbp; /* temporary var */ + uint8_t *rsp_buf; + uint8_t *rbp; /* temporary var */ struct scsi_inquiry *inq; - uint32_t len, - page83_len, - rqst_len, - rtn_len; + uint32_t len; + uint32_t page83_len; + uint32_t rqst_len; + uint32_t rtn_len; struct vpd_hdr *vhp; struct vpd_desc vd; size_t scsi_len; @@ -188,7 +188,7 @@ spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) */ scsi_len = ((strlen(cmd->c_lu->l_targ->s_targ_base) + 1) + 3) & ~3; page83_len = (sizeof (struct vpd_desc) * 6) + scsi_len + - (lu->l_guid_len * 3) + (sizeof (uint32_t) * 2); + (lu->l_guid_len * 3) + (sizeof (uint32_t) * 2); /* * We always allocate enough space so that the code can create @@ -396,8 +396,8 @@ spc_inquiry(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) bcopy(&vd, rbp, sizeof (vd)); rbp += sizeof (vd); - rbp[2] = hibyte(loword(cmd->c_lu->l_targ->s_tp_grp)); - rbp[3] = lobyte(loword(cmd->c_lu->l_targ->s_tp_grp)); + rbp[2] = hibyte(loword(cmd->c_lu->l_targ->s_tpgt)); + rbp[3] = lobyte(loword(cmd->c_lu->l_targ->s_tpgt)); rbp += vd.len; /* ---- VPD descriptor ---- */ @@ -588,16 +588,16 @@ spc_report_luns(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) { int expected_data; uint8_t *buf = NULL; - int entries = 0, - len, - len_network, - select, - lun_idx, - lun_val; + int entries = 0; + int len; + int len_network; + int select; + int lun_idx; + int lun_val; char *str; - tgt_node_t *targ, - *lun_list, - *lun; + tgt_node_t *targ; + tgt_node_t *lun_list; + tgt_node_t *lun; /* * SPC-3 Revision 21c section 6.21 @@ -702,9 +702,9 @@ spc_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) rtpg_hdr_t *r; rtpg_desc_t *dp; rtpg_targ_desc_t *tp; - int rqst_len, - alloc_len, - i; + int rqst_len; + int alloc_len; + int i; t10_lu_common_t *lu = cmd->c_lu->l_common; t10_lu_impl_t *lu_per; @@ -778,8 +778,8 @@ spc_report_tpgs(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) tp = &dp->targ_list[0]; lu_per = avl_first(&lu->l_all_open); do { - tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tp_grp)); - tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tp_grp)); + tp->rel_tpi[0] = hibyte(loword(lu_per->l_targ->s_tpgt)); + tp->rel_tpi[1] = lobyte(loword(lu_per->l_targ->s_tpgt)); lu_per = AVL_NEXT(&lu->l_all_open, lu_per); tp++; } while (lu_per != NULL); @@ -815,7 +815,7 @@ spc_send_diag(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) trans_send_complete(cmd, STATUS_GOOD); } -static void +void spc_free(emul_handle_t id) { free(id); @@ -1139,14 +1139,13 @@ spc_encode_lu_addr(uint8_t *buf, int select_field, uint32_t lun) * SAM-3 revision 14, Section 4.9.7. * 14-bit flat address space. */ - buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE | - (lun >> 8 & 0x3f); + buf[0] = SCSI_REPORTLUNS_ADDRESS_FLAT_SPACE | (lun >> 8 & 0x3f); buf[1] = lun & 0xff; } else if (select_field == SCSI_REPORTLUNS_SELECT_ALL) { buf[0] = SCSI_REPORTLUNS_ADDRESS_EXTENDED_UNIT | - SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B; + SCSI_REPORTLUNS_ADDRESS_EXTENDED_6B; /* * 32-bit limitation. This format should be able to * handle a 40-bit LUN. diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h index 810dd7174f..1fe897f2fa 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc.h @@ -20,15 +20,19 @@ */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _SPC_H -#define _SPC_H +#ifndef _T10_SPC_H +#define _T10_SPC_H #pragma ident "%Z%%M% %I% %E% SMI" +#ifdef __cplusplus +extern "C" { +#endif + /* * []------------------------------------------------------------------[] * | SPC-3 | @@ -98,33 +102,60 @@ Boolean_t spc_encode_lu_addr(uint8_t *buf, int select_field, uint32_t lun); * | by the code. | * []------------------------------------------------------------------[] */ -#define SPC_ASC_INVALID_CDB 0x24 -#define SPC_ASCQ_INVALID_CDB 0x00 -#define SPC_ASC_PWR_RESET 0x29 -#define SPC_ASCQ_PWR_RESET 0x00 -#define SPC_ASC_PWR_ON 0x29 -#define SPC_ASCQ_PWR_ON 0x01 -#define SPC_ASC_BUS_RESET 0x29 -#define SPC_ASCQ_BUS_RESET 0x02 #define SPC_ASC_FM_DETECTED 0x00 /* file-mark detected */ #define SPC_ASCQ_FM_DETECTED 0x01 + #define SPC_ASC_EOP 0x00 /* end-of-partition/medium detected */ #define SPC_ASCQ_EOP 0x02 -#define SPC_ASC_WRITE_ERROR 0x0c -#define SPC_ASCQ_WRITE_ERROR 0x00 -#define SPC_ASC_CAP_CHANGE 0x2a -#define SPC_ASCQ_CAP_CHANGE 0x09 + #define SPC_ASC_IN_PROG 0x04 #define SPC_ASCQ_IN_PROG 0x07 -#define SPC_ASC_DATA_PATH 0x41 -#define SPC_ASCQ_DATA_PATH 0x00 + +#define SPC_ASC_WRITE_ERROR 0x0c +#define SPC_ASCQ_WRITE_ERROR 0x00 + +#define SPC_ASC_PARAM_LIST_LEN 0x1a /* Parameter List Length Error */ +#define SPC_ASCQ_PARAM_LIST_LEN 0x00 + #define SPC_ASC_MISCOMPARE 0x1d #define SPC_ASCQ_MISCOMPARE 0x00 + #define SPC_ASC_INVALID_LU 0x20 #define SPC_ASCQ_INVALID_LU 0x09 + #define SPC_ASC_BLOCK_RANGE 0x21 #define SPC_ASCQ_BLOCK_RANGE 0x00 +#define SPC_ASC_INVALID_FIELD_IN_PARAMETER_LIST 0x26 +#define SPC_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST 0x00 + +#define SPC_ASC_INVALID_CDB 0x24 +#define SPC_ASCQ_INVALID_CDB 0x00 + +#define SPC_ASC_PARAMETERS_CHANGED 0x2a +#define SPC_ASCQ_RES_PREEMPTED 0x03 +#define SPC_ASCQ_RES_RELEASED 0x04 + +#define SPC_ASC_PWR_RESET 0x29 +#define SPC_ASCQ_PWR_RESET 0x00 + +#define SPC_ASC_PWR_ON 0x29 +#define SPC_ASCQ_PWR_ON 0x01 + +#define SPC_ASC_BUS_RESET 0x29 +#define SPC_ASCQ_BUS_RESET 0x02 + +#define SPC_ASC_CAP_CHANGE 0x2a +#define SPC_ASCQ_CAP_CHANGE 0x09 + +#define SPC_ASC_DATA_PATH 0x41 +#define SPC_ASCQ_DATA_PATH 0x00 + +#define SPC_ASC_MEMORY_OUT_OF 0x55 /* Auxillary Memory Out Of Space */ +#define SPC_ASCQ_MEMORY_OUT_OF 0x00 +#define SPC_ASCQ_RESERVATION_FAIL 0x02 + + /* * []------------------------------------------------------------------[] * | SAM-3, revision 14, section 5.2 - Command descriptor block (CDB) | @@ -417,4 +448,8 @@ struct mode_info_ctrl { #define SCSI_REPORTLUNS_ADDRESS_EXTENDED_MASK 0x30 #define SCSI_REPORTLUNS_SELECT_ALL 0x02 -#endif /* _SPC_H */ +#ifdef __cplusplus +} +#endif + +#endif /* _T10_SPC_H */ diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c new file mode 100644 index 0000000000..99da20d627 --- /dev/null +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.c @@ -0,0 +1,1913 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * []------------------------------------------------------------------[] + * | Implementation of SPC-3 Persistent Reserve emulation | + * []------------------------------------------------------------------[] + */ +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/asynch.h> +#include <sys/param.h> +#include <sys/sysmacros.h> +#include <strings.h> +#include <unistd.h> +#include <pthread.h> +#include <assert.h> + +#include <sys/scsi/generic/sense.h> +#include <sys/scsi/generic/status.h> +#include <sys/scsi/generic/inquiry.h> +#include <sys/scsi/generic/mode.h> +#include <sys/scsi/generic/commands.h> +#include <sys/scsi/generic/persist.h> + +#include "t10.h" +#include "t10_spc.h" +#include "t10_spc_pr.h" +#include "t10_sbc.h" +#include "target.h" + +/* + * External declarations + */ +void spc_free(emul_handle_t id); +void sbc_cmd(t10_cmd_t *, uint8_t *, size_t); +void sbc_cmd_reserved(t10_cmd_t *, uint8_t *, size_t); + +extern target_queue_t *mgmtq; + +/* + * Forward declarations + */ +static + spc_pr_rsrv_t *spc_pr_rsrv_find(scsi3_pgr_t *, uint64_t, uint64_t, char *); +static + spc_pr_rsrv_t *spc_pr_rsrv_alloc(scsi3_pgr_t *, uint64_t, uint64_t, char *, + uint8_t, uint8_t); +static + spc_pr_key_t *spc_pr_key_find(scsi3_pgr_t *, uint64_t, uint64_t, char *); +static + spc_pr_key_t *spc_pr_key_alloc(scsi3_pgr_t *, uint64_t, uint64_t, char *); + +static void spc_pr_rsrv_release(t10_cmd_t *, scsi3_pgr_t *, spc_pr_rsrv_t *); +static void spc_pr_key_free(scsi3_pgr_t *, spc_pr_key_t *); +static void spc_pr_rsrv_free(scsi3_pgr_t *, spc_pr_rsrv_t *); +static void spc_pr_erase(scsi3_pgr_t *); +static void spc_pr_key_rsrv_init(scsi3_pgr_t *); + +static int spc_pr_register(t10_cmd_t *, void *, size_t); +static int spc_pr_reserve(t10_cmd_t *, void *, size_t); +static int spc_pr_release(t10_cmd_t *, void *, size_t); +static int spc_pr_clear(t10_cmd_t *, void *, size_t); +static int spc_pr_preempt(t10_cmd_t *, void *, size_t); +static int spc_pr_register_and_move(t10_cmd_t *, void *, size_t); + +static int spc_pr_in_readkeys(char *, scsi3_pgr_t *, void *, uint16_t); +static int spc_pr_in_readrsrv(char *, scsi3_pgr_t *, void *, uint16_t); +static int spc_pr_in_repcap(char *, scsi3_pgr_t *, void *, uint16_t); +static int spc_pr_in_fullstat(char *, scsi3_pgr_t *, void *, uint16_t); + +static int spc_pgr_isconflict(uint8_t *, uint_t); +Boolean_t spc_pr_write(t10_cmd_t *); + +/* + * []---- + * | spc_pgr_check -- PERSISTENT_RESERVE {IN|OUT} check of I_T_L + * | Refer to SPC-3, Section ?.?, Tables ?? and ?? + * []---- + */ +Boolean_t +spc_pgr_check(t10_cmd_t *cmd, uint8_t *cdb) +{ + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_rsrv_t *rsrv; + Boolean_t conflict = False; + + /* + * Check reservation commands. + */ + switch (cdb[0]) { + /* + * Always dis-allow these commands. + */ + case SCMD_RESERVE: + case SCMD_RESERVE_G1: + case SCMD_RELEASE: + case SCMD_RELEASE_G1: + conflict = True; + goto done; + + /* + * Always allow these commands. + */ + case SCMD_PERSISTENT_RESERVE_IN: + case SCMD_PERSISTENT_RESERVE_OUT: + conflict = False; + goto done; + } + + /* + * If no reservations exist, allow all remaining command types. + */ + assert(res->res_type == RT_PGR); + if (pgr->pgr_numrsrv == 0) { + conflict = False; + goto done; + } + + /* + * At this point we know there is at least one reservation. + * If there is no reservation set on this service delivery + * port then conflict all remaining command types. + */ + if (!(rsrv = spc_pr_rsrv_find(pgr, 0, 0, T10_PGR_TID(cmd)))) { + queue_prt(mgmtq, Q_PR_IO, "PGR Reserved on other port\n", + "\t%016x:%s\n", T10_PGR_ISID(cmd), T10_PGR_TID(cmd)); + conflict = True; + goto done; + } + + /* + * Check the command against the reservation type for this port. + */ + switch (rsrv->r_type) { + case PGR_TYPE_WR_EX: + case PGR_TYPE_EX_AC: + if (T10_PGR_ISID(cmd) == rsrv->r_isid) + conflict = False; + else + conflict = spc_pgr_isconflict(cdb, + rsrv->r_type); + break; + case PGR_TYPE_WR_EX_RO: + case PGR_TYPE_EX_AC_RO: + if (spc_pr_key_find( + pgr, 0, T10_PGR_ISID(cmd), T10_PGR_TID(cmd))) + conflict = False; + else + conflict = spc_pgr_isconflict(cdb, + rsrv->r_type); + break; + case PGR_TYPE_WR_EX_AR: + case PGR_TYPE_EX_AC_AR: + if (spc_pr_key_find(pgr, 0, 0, T10_PGR_TID(cmd))) + conflict = False; + else + conflict = spc_pgr_isconflict(cdb, + rsrv->r_type); + break; + default: + conflict = True; + break; + } + +done: + queue_prt(mgmtq, Q_PR_IO, "PGR%d LUN%d CDB:%s - spc_pgr_check(%s)\n", + cmd->c_lu->l_targ->s_targ_num, + cmd->c_lu->l_common->l_num, + cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL + ? "(no name)" + : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name, + (conflict) ? "Conflict" : "Allowed"); + + return (conflict); +} + +/* + * []---- + * | spc_pgr_isconflict + * | PGR reservation conflict checking. + * | SPC-3, Revision 23, Table 31 + * []---- + */ +static int +spc_pgr_isconflict(uint8_t *cdb, uint_t type) +{ + Boolean_t conflict = False; + + switch (cdb[0]) { + case SCMD_FORMAT: + case SCMD_EXTENDED_COPY: + case SCMD_LOG_SELECT_G1: + case SCMD_MODE_SELECT: + case SCMD_MODE_SELECT_G1: + case SCMD_MODE_SENSE: + case SCMD_MODE_SENSE_G1: + case SCMD_READ_ATTRIBUTE: + case SCMD_READ_BUFFER: + case SCMD_GDIAG: /* SCMD_RECEIVE_DIAGNOSTIC_RESULTS */ + case SCMD_SDIAG: /* SCMD_SEND_DIAGNOSTIC_RESULTS */ + case SCMD_WRITE_ATTRIBUTE: + case SCMD_WRITE_BUFFER: + conflict = True; + break; + + case SCMD_DOORLOCK: /* SCMD_PREVENT_ALLOW_MEDIA_REMOVAL */ + /* + * As per SPC-3, Revision 23, Table 31 + * (prevent <> 0) + */ + conflict = (cdb[4] & 0x1) ? True: False; + break; + + case SCMD_REPORT_TARGET_PORT_GROUPS: /* SCMD_REPORT_ */ + /* + * As pee SPC-3, Revision 23, Section 6.23 + */ + switch ((cdb[1] & 0x03)) { + /* SCMD_REPORT_SUPPORTED_OPERATION_CODES */ + case 0x0c: + /* SCMD_REPORT_SUPPORTED_MANAGEMENT_FUNCTIONS */ + case 0x0d: + + conflict = True; + break; + } + break; + + case SCMD_SET_DEVICE: + /* + * SPC-3, Revision 23, Section 6.29 + */ + switch ((cdb[1] & 0x1F)) { + case SCMD_SET_DEVICE_IDENTIFIER: + case SCMD_SET_PRIORITY: + case SCMD_SET_TARGET_PORT_GROUPS: + case SCMD_SET_TIMESTAMP: + conflict = True; + break; + } + break; + + case SCMD_READ: + case SCMD_READ_G1: + case SCMD_READ_G4: + if (type == PGR_TYPE_EX_AC || type == PGR_TYPE_EX_AC_RO) + conflict = True; + break; + } + + return (conflict); +} + + +/* + * []---- + * | spc_cmd_pr_in -- PERSISTENT_RESERVE IN + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/*ARGSUSED*/ +void +spc_cmd_pr_in(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) +{ + scsi_cdb_prin_t *p_prin = (scsi_cdb_prin_t *)cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + uint16_t alen; + size_t len; + void *buf; + Boolean_t status; + + /* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11 PERSISTENCE RESERVE IN + * Need to generate a CHECK CONDITION with ILLEGAL REQUEST + * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is + * true. + * (1) The SERVICE ACTION field is 004h - 01fh, + * (2) The reserved area in byte 1 is set, + * (3) The reserved area in bytes 2 thru 6 are set, + * (4) If any of the reserved bits in the CONTROL byte are set. + */ + if ((p_prin->action >= 0x4) || p_prin->resbits || p_prin->resbytes[0] || + p_prin->resbytes[1] || p_prin->resbytes[2] || p_prin->resbytes[3] || + p_prin->resbytes[4] || p_prin->control) { + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11 PERSISTENCE RESERVE IN + * Acquire ALLOCATION LENGTH from bytes 7, 8 + * A zero(0) length allocation is not an error and we should just + * acknowledge the operation. + */ + if ((alen = SCSI_READ16(p_prin->alloc_len)) == 0) { + queue_prt(mgmtq, Q_PR_IO, + "PGR:%d LUN:%d CDB:%s - spc_cmd_pr_in, len = 0\n", + cmd->c_lu->l_targ->s_targ_num, + cmd->c_lu->l_common->l_num, + cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name == NULL + ? "(no name)" + : cmd->c_lu->l_cmd_table[cmd->c_cdb[0]].cmd_name); + + trans_send_complete(cmd, STATUS_GOOD); + return; + } + + /* + * Allocate space with an alignment that will work for any casting. + */ + if ((buf = memalign(sizeof (void *), alen)) == NULL) { + /* + * Lack of memory is not fatal, just too busy + */ + trans_send_complete(cmd, STATUS_BUSY); + return; + } else { + bzero(buf, alen); + } + + /* + * Start processing, lock reservation + */ + pthread_rwlock_rdlock(&res->res_rwlock); + + /* + * Per SPC-3, Revision 23, Table 102, validate ranget of service actions + */ + switch (p_prin->action) { + case PR_IN_READ_KEYS: + len = spc_pr_in_readkeys( + T10_PGR_TID(cmd), pgr, buf, alen); + break; + case PR_IN_READ_RESERVATION: + len = spc_pr_in_readrsrv( + T10_PGR_TID(cmd), pgr, buf, alen); + break; + case PR_IN_REPORT_CAPABILITIES: + len = spc_pr_in_repcap( + T10_PGR_TID(cmd), pgr, buf, alen); + break; + case PR_IN_READ_FULL_STATUS: + len = spc_pr_in_fullstat( + T10_PGR_TID(cmd), pgr, buf, alen); + break; + default: + pthread_rwlock_unlock(&res->res_rwlock); + spc_free(buf); + + /* + * Fail command + */ + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Complete processing, unlock reservation + */ + pthread_rwlock_unlock(&res->res_rwlock); + + /* + * Now send the selected Persistent Reservation response back + */ + if (trans_send_datain(cmd, buf, alen, 0, spc_free, True, buf) == False) + trans_send_complete(cmd, STATUS_BUSY); +} + +/* + * []---- + * | spc_pr_in_readkey - + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static int +spc_pr_in_readkeys(char *transportID, scsi3_pgr_t *pgr, void *bp, + uint16_t alloc_len) +{ + int i = 0, max_buf_keys, hsize; + scsi_prin_readrsrv_t *buf = (scsi_prin_readrsrv_t *)bp; + spc_pr_key_t *key; + + hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len); + max_buf_keys = ((int)alloc_len - hsize) / sizeof (key->k_key); + + queue_prt(mgmtq, Q_PR_IO, + "PGRIN readkeys - transportID=%s\n", transportID); + + if (pgr->pgr_numkeys) + for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + key != (spc_pr_key_t *)&pgr->pgr_keylist; + key = (spc_pr_key_t *)key->k_link.lnk_fwd) { + + if (strcmp(key->k_transportID, transportID)) + continue; + + if (i < max_buf_keys) + SCSI_WRITE64(buf->res_key_list[i].reservation_key, + key->k_key); + + queue_prt(mgmtq, Q_PR_IO, + "PGRIN readkeys - key:%016x, isid:%016x\n", + key->k_key, key->k_isid); + + i++; + } + + SCSI_WRITE32(buf->add_len, i * sizeof (key->k_key)); + SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation); + + return (hsize + min(SCSI_READ32(buf->add_len), + (int)(max_buf_keys * sizeof (key->k_key)))); +} + +/* + * []---- + * | spc_pr_in_readresv - + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static int +spc_pr_in_readrsrv( + char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len) +{ + int i = 0, max_buf_rsrv, hsize; + spc_pr_rsrv_t *rsrv; + scsi_prin_readrsrv_t *buf = (scsi_prin_readrsrv_t *)bp; + + hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len); + max_buf_rsrv = ((int)alloc_len - hsize) / sizeof (scsi_prin_rsrvdesc_t); + + queue_prt(mgmtq, Q_PR_IO, + "PGRIN readrsrv - transportID=%s\n", transportID); + + if (pgr->pgr_numrsrv) + for (rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd; + rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist; + rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) { + + if (strcmp(rsrv->r_transportID, transportID)) + continue; + + if (i < max_buf_rsrv) { + SCSI_WRITE64(buf->res_key_list[i].reservation_key, + rsrv->r_key); + buf->res_key_list[i].scope = rsrv->r_scope; + buf->res_key_list[i].type = rsrv->r_type; + } + + queue_prt(mgmtq, Q_PR_IO, + "PGRIN readrsrv - " + "key:%016x isid:%016x scope:%d type:%d \n", + rsrv->r_key, rsrv->r_isid, rsrv->r_scope, rsrv->r_type); + + i++; + } + + SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t)); + SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation); + + return (hsize + min(SCSI_READ32(buf->add_len), + (int)(max_buf_rsrv * sizeof (scsi_prin_rsrvdesc_t)))); +} + +/* + * []---- + * | spc_pr_in_repcap - + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/* + */ +static int +spc_pr_in_repcap( + char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len) +{ + scsi_prin_rpt_cap_t *buf = (scsi_prin_rpt_cap_t *)bp; + + buf->crh = 0; /* Supports Reserve / Release */ + buf->sip_c = 1; /* Specify Initiator Ports Capable */ + buf->atp_c = 1; /* All Target Ports Capable */ + buf->ptpl_c = 1; /* Persist Through Power Loss C */ + buf->tmv = 1; /* Type Mask Valid */ + buf->ptpl_a = pgr_persist; /* Persist Though Power Loss Active */ + buf->pr_type.wr_ex = 1; /* Write Exclusve */ + buf->pr_type.ex_ac = 1; /* Exclusive Access */ + buf->pr_type.wr_ex_ro = 1; /* Write Exclusive Registrants Only */ + buf->pr_type.ex_ac_ro = 1; /* Exclusive Access Registrants Only */ + buf->pr_type.wr_ex_ar = 1; /* Write Exclusive All Registrants */ + buf->pr_type.ex_ac_ar = 1; /* Exclusive Access All Registrants */ + + SCSI_WRITE16(buf->length, sizeof (scsi_prin_rpt_cap_t)); + + return (sizeof (scsi_prin_rpt_cap_t)); +} + +/* + * []---- + * | spc_pr_in_fullstat - + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/* + */ +static int +spc_pr_in_fullstat( + char *transportID, scsi3_pgr_t *pgr, void *bp, uint16_t alloc_len) +{ + int i = 0, max_buf_rsrv, hsize; + spc_pr_rsrv_t *rsrv; + scsi_prin_full_status_t *buf = (scsi_prin_full_status_t *)bp; + + hsize = sizeof (buf->PRgeneration) + sizeof (buf->add_len); + max_buf_rsrv = ((int)alloc_len - hsize) / + sizeof (scsi_prin_full_status_t); + + if (pgr->pgr_numrsrv) + for (i = 0, rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd; + rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist; + rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) { + + if (i < max_buf_rsrv) { + SCSI_WRITE64(buf->full_desc[i].reservation_key, + rsrv->r_key); + buf->full_desc[i].all_tg_pt = 1; + buf->full_desc[i].r_holder = + strcmp(rsrv->r_transportID, transportID) ? 0 : 1; + buf->full_desc[i].scope = rsrv->r_scope; + buf->full_desc[i].type = rsrv->r_type; + SCSI_WRITE16(buf->full_desc[i].rel_tgt_port_id, 0); + SCSI_WRITE32(buf->full_desc[i].add_len, + sizeof (scsi_transport_id_t)); + buf->full_desc[i].trans_id.protocol_id = + iSCSI_PROTOCOL_ID; + buf->full_desc[i].trans_id.format_code = + WW_UID_DEVICE_NAME; + SCSI_WRITE16(buf->full_desc[i].trans_id.add_len, 0); + sprintf(buf->full_desc[i].trans_id.iscsi_name, ""); + } + + i++; + } + + SCSI_WRITE32(buf->add_len, i * sizeof (scsi_prin_rsrvdesc_t)); + SCSI_WRITE32(buf->PRgeneration, pgr->pgr_generation); + + return (hsize + min(SCSI_READ32(buf->add_len), + (int)(max_buf_rsrv * sizeof (scsi_prin_rsrvdesc_t)))); + +} + +/* + * []---- + * | spc_cmd_pr_out -- PERSISTENT_RESERVE OUT + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/*ARGSUSED*/ +void +spc_cmd_pr_out(t10_cmd_t *cmd, uint8_t *cdb, size_t cdb_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + size_t len; + void *buf; + + /* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12 PERSISTENCE RESERVE OUT + * Need to generate a CHECK CONDITION with ILLEGAL REQUEST + * and INVALID FIELD IN CDB (0x24/0x00) if any of the following is + * true. + * (1) The SERVICE ACTION field is 008h - 01fh, + * (2) The reserved area in byte 1 is set, + * (3) The TYPE and SCOPE fields are invalid, + * (4) The reserved area in bytes 3 and 4 are set, + * (5) If any of the reserved bits in the CONTROL byte are set. + */ + if ((p_prout->action >= 0x8) || p_prout->resbits || + (p_prout->type >= 0x9) || + (p_prout->scope >= 0x3) || p_prout->control) { + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12 PERSISTENCE RESERVE OUT + * Acquire ALLOCATION LENGTH from bytes 5 thru 8 + */ + len = SCSI_READ32(p_prout->param_len); + + /* + * Parameter list length shall contain 24 (0x18), + * the SPEC_I_PIT is zero (it is because we don't support SIP_C)) + * the service action is not REGISTER AND MOVE + */ + if ((p_prout->action != PR_OUT_REGISTER_MOVE) && (len != 24)) { + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, SPC_ASC_PARAM_LIST_LEN, 0x00); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.3.3 Persistent Reservation Scope + * SCOPE field shall be set to LU_SCOPE + */ + if (p_prout->scope != PR_LU_SCOPE) { + pthread_rwlock_unlock(&res->res_rwlock); + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, SPC_ASC_INVALID_CDB, 0x00); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Allocate space with an alignment that will work for any casting. + */ + if ((buf = memalign(sizeof (void *), len)) == NULL) { + /* + * Lack of memory is not fatal, just too busy + */ + trans_send_complete(cmd, STATUS_BUSY); + return; + } + + /* + * Now request the Persistent Reserve OUT parameter list + */ + if (trans_rqst_dataout(cmd, buf, len, 0, buf, spc_free) == False) + trans_send_complete(cmd, STATUS_BUSY); +} + +/* + * []---- + * | spc_cmd_pr_out_data -- DataIn phase of PERSISTENT_RESERVE OUT command + * []---- + */ +/*ARGSUSED*/ +void +spc_cmd_pr_out_data(t10_cmd_t *cmd, emul_handle_t id, size_t offset, char *data, + size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + t10_lu_impl_t *lu; + int status; + + /* + * If this is the first time using the persistance data, + * initialize the reservation and resource key queues + */ + pthread_rwlock_wrlock(&res->res_rwlock); + if (pgr->pgr_rsrvlist.lnk_fwd == NULL) { + spc_pr_key_rsrv_init(pgr); + } + + /* + * Now process the action. + */ + switch (p_prout->action) { + case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY: + case PR_OUT_REGISTER: + /* + * PR_OUT_REGISTER_IGNORE differs from PR_OUT_REGISTER + * in that the reservation_key is ignored. + */ + status = spc_pr_register(cmd, data, data_len); + break; + + case PR_OUT_RESERVE: + status = spc_pr_reserve(cmd, data, data_len); + break; + + case PR_OUT_RELEASE: + status = spc_pr_release(cmd, data, data_len); + break; + + case PR_OUT_CLEAR: + status = spc_pr_clear(cmd, data, data_len); + break; + + case PR_OUT_PREEMPT_ABORT: + case PR_OUT_PREEMPT: + /* + * PR_OUT_PREEMPT_ABORT differs from PR_OUT_PREEMPT + * in that all current acitivy for the preempted + * Initiators will be terminated. + */ + status = spc_pr_preempt(cmd, data, data_len); + break; + + case PR_OUT_REGISTER_MOVE: + /* + * PR_OUT_REGISTER_MOVE registers a key for another I_T + */ + status = spc_pr_register_and_move(cmd, data, data_len); + break; + } + + /* + * Check status of action performed. + */ + if (status == STATUS_CHECK) { + /* + * Check condition required. + */ + pthread_rwlock_unlock(&res->res_rwlock); + spc_sense_create(cmd, KEY_ILLEGAL_REQUEST, 0); + spc_sense_ascq(cmd, cmd->c_lu->l_asc, cmd->c_lu->l_ascq); + trans_send_complete(cmd, STATUS_CHECK); + return; + } + + /* + * Handle Failed processing status + */ + if (status != STATUS_GOOD) { + pthread_rwlock_unlock(&res->res_rwlock); + trans_send_complete(cmd, status); + return; + } + + /* + * Successful, bump the PRgeneration value + */ + if (p_prout->action != PR_OUT_RESERVE && + p_prout->action != PR_OUT_RELEASE) + pgr->pgr_generation++; + + /* + * If Activate Persist Through Power Loss (APTPL) is set, persist + * this PGR data on disk + */ + if (plist->aptpl || pgr->pgr_aptpl) + spc_pr_write(cmd); + + /* + * When the last registration is removed, PGR is no longer + * active and we must reset the reservation type. + */ + if (pgr->pgr_numkeys == 0 && pgr->pgr_numrsrv == 0) { + res->res_type = RT_NONE; + pgr->pgr_aptpl = 0; + } else { + res->res_type = RT_PGR; + } + + /* + * Set the command dispatcher according to the reservation type + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + lu->l_cmd = (res->res_type == RT_NONE) + ? sbc_cmd + : sbc_cmd_reserved; + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); + } while (lu != NULL); + + queue_prt(mgmtq, Q_PR_IO, "PGROUT:%d LUN:%d action:%s\n", + cmd->c_lu->l_targ->s_targ_num, + cmd->c_lu->l_common->l_num, + (p_prout->action == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY) + ? "Register & ignore existing key" + : (p_prout->action == PR_OUT_REGISTER) + ? "Register" + : (p_prout->action == PR_OUT_RESERVE) + ? "Reserve" + : (p_prout->action == PR_OUT_RELEASE) + ? "Release" + : (p_prout->action == PR_OUT_CLEAR) + ? "Clear" + : (p_prout->action == PR_OUT_PREEMPT_ABORT) + ? "Preempt & abort" + : (p_prout->action == PR_OUT_PREEMPT) + ? "Preempt" + : (p_prout->action == PR_OUT_REGISTER_MOVE) + ? "Register & move" + : "Uknown"); + + /* + * Processing is complete, release mutex + */ + pthread_rwlock_unlock(&res->res_rwlock); + + /* + * Send back a succesful response + */ + trans_send_complete(cmd, STATUS_GOOD); +} + +/* + * []---- + * | spc_pr_register + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static int +spc_pr_register(t10_cmd_t *cmd, void *data, size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_rsrv_t *rsrv; + spc_pr_key_t *key; + uint64_t reservation_key; + uint64_t service_key; + t10_lu_impl_t *lu; + t10_targ_impl_t *ti; + + /* + * Validate Persistent Reserver Out parameter list + */ + if (plist->obsolete1[0] || plist->obsolete1[1] || + plist->obsolete1[2] || plist->obsolete1[3] || + plist->resbits1 || plist->resbits2 || plist->resbytes1 || + plist->obsolete2[0] || plist->obsolete2[1]) { + cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST; + cmd->c_lu->l_asc = SPC_ASC_INVALID_CDB; + cmd->c_lu->l_ascq = 0; + return (STATUS_CHECK); + } + + /* + * Determine if Activate Persist Trhough Power Loss (APTPL) + * is valid for this device server. + */ + if (plist->aptpl && (pgr_persist == 0)) { + /* pgr - define SCSI-3 error codes */ + cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST; + cmd->c_lu->l_asc = SPC_ASC_INVALID_FIELD_IN_PARAMETER_LIST; + cmd->c_lu->l_ascq = 0; + return (STATUS_CHECK); + } + + /* + * Get reservation values + */ + reservation_key = SCSI_READ64(plist->reservation_key); + service_key = SCSI_READ64(plist->service_key); + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: register reservation:%016x, key:%016x\n", + reservation_key, service_key); + + /* + * We may need register all initiators, depending on ALL_TG_TP + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + /* + * Find specified key + */ + ti = lu->l_targ; + key = spc_pr_key_find(pgr, 0, ti->s_isid, ti->s_transportID); + if (key) { + /* + * What about ALL_TG_TP? + */ + if (plist->all_tg_pt || + (key->k_isid == T10_PGR_ISID(cmd))) { + + if (p_prout->action == PR_OUT_REGISTER && + key->k_key != reservation_key) { + /* + * The Initiator did not specify the + * existing key. Reservation conflict. + */ + return (STATUS_RESERVATION_CONFLICT); + } + /* + * Change existing key ? + */ + if (service_key) { + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: change " + "old:%016x = new:%016x\n", + key->k_key, service_key); + + /* + * Overwrite (change) key + */ + key->k_key = service_key; + + } else { + /* + * Remove existing key + * NOTE: If we own the reservation then + * we must release it. + */ + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: delete " + "old:%016x = new:%016x\n", + key->k_key, service_key); + + rsrv = spc_pr_rsrv_find(pgr, 0, + ti->s_isid, ti->s_transportID); + if (rsrv) { + spc_pr_rsrv_release( + cmd, pgr, rsrv); + spc_pr_key_free(pgr, key); + } + } + } + } else { + /* + * What about ALL_TG_TP? + */ + if (plist->all_tg_pt || + (ti->s_isid == T10_PGR_ISID(cmd))) { + /* + * Process request from un-registered Initiator. + */ + if ((p_prout->action == PR_OUT_REGISTER) && + (reservation_key || service_key == 0)) { + /* + * Unregistered initiator is attempting + * to modify a key. + */ + return (STATUS_RESERVATION_CONFLICT); + } + + /* + * Allocate new key. + */ + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: new:%016x\n", service_key); + + key = spc_pr_key_alloc(pgr, service_key, + ti->s_isid, ti->s_transportID); + if (key == NULL) { + /* pgr - define SCSI-3 error codes */ + cmd->c_lu->l_status = + KEY_ABORTED_COMMAND; + cmd->c_lu->l_asc = + SPC_ASC_MEMORY_OUT_OF; + cmd->c_lu->l_ascq = + SPC_ASCQ_RESERVATION_FAIL; + return (STATUS_CHECK); + } + } + } + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); + } while (lu != NULL); + + /* + * Apply the last valid APTPL bit + * SPC-3, Revision 23 + * Section 5.6.4.1 Preserving persistent reservervations and + * registrations through power loss + */ + pgr->pgr_aptpl = plist->aptpl; + + return (STATUS_GOOD); +} + +/* + * []---- + * | spc_pr_reserve + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/* ARGSUSED */ +static int +spc_pr_reserve(t10_cmd_t *cmd, void *data, size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_rsrv_t *rsrv; + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + uint64_t reservation_key; + int status; + + /* + * Do not allow an unregistered initiator to + * make a reservation. + */ + reservation_key = SCSI_READ64(plist->reservation_key); + if (!spc_pr_key_find( + pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd))) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: reserve reservation:%016x not found\n", + reservation_key); + + return (STATUS_RESERVATION_CONFLICT); + } else { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: reserve reservation:%016x\n", reservation_key); + + } + + /* + * See if there is a reservation on this port by + * another Initiator. There can be only one LU_SCOPE + * reservation per ITL. We do not support extents. + */ + if (rsrv = spc_pr_rsrv_find(pgr, 0, 0, T10_PGR_TID(cmd))) { + if (rsrv->r_isid != T10_PGR_ISID(cmd)) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: reserve %016x != %016x:%s\n", + rsrv->r_isid, T10_PGR_ISID(cmd), + T10_PGR_TID(cmd)); + + return (STATUS_RESERVATION_CONFLICT); + } + } + + /* + * At this point there is either no reservation or the + * reservation is held by this Initiator. + */ + if (rsrv != NULL) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT reserve(+) - transportID=%s\n" + "\tkey:%016x isid:%016x scope:%d type:%d \n", + rsrv->r_transportID, rsrv->r_key, rsrv->r_isid, + rsrv->r_scope, rsrv->r_type); + + /* + * An Initiator cannot re-reserve. It must first + * release. But if its' type and scope match then + * return STATUS_GOOD. + */ + if (rsrv->r_type == p_prout->type && + rsrv->r_scope == p_prout->scope) { + status = STATUS_GOOD; + } else { + status = STATUS_RESERVATION_CONFLICT; + } + } else { + /* + * No reservation exists. Establish a new one. + */ + queue_prt(mgmtq, Q_PR_IO, + "PGROUT reserve - transportID=%s\n" + "\tkey:%016x isid:%016x scope:%d type:%d \n", + T10_PGR_TID(cmd), reservation_key, T10_PGR_ISID(cmd), + p_prout->scope, p_prout->type); + + rsrv = spc_pr_rsrv_alloc(pgr, reservation_key, + T10_PGR_ISID(cmd), T10_PGR_TID(cmd), + p_prout->scope, p_prout->type); + if (rsrv == NULL) { + cmd->c_lu->l_status = KEY_ABORTED_COMMAND; + cmd->c_lu->l_asc = SPC_ASC_MEMORY_OUT_OF; + cmd->c_lu->l_ascq = SPC_ASCQ_RESERVATION_FAIL; + status = STATUS_CHECK; + } else { + status = STATUS_GOOD; + } + } + + return (status); +} + +/* + * []---- + * | spc_pr_release + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static int +spc_pr_release(t10_cmd_t *cmd, void *data, size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_rsrv_t *rsrv; + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + uint64_t reservation_key; + int status; + + /* + * Do not allow an unregistered initiator to attempting to + * release a reservation. + */ + reservation_key = SCSI_READ64(plist->reservation_key); + if (!spc_pr_key_find( + pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd))) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: release reservation:%016x not found\n", + reservation_key); + + return (STATUS_RESERVATION_CONFLICT); + } else { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: release reservation:%016x\n", reservation_key); + } + + if (!(rsrv = spc_pr_rsrv_find( + pgr, 0, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)))) { + /* + * Releasing a non-existent reservation is allowed. + */ + status = STATUS_GOOD; + + } else if (p_prout->scope != rsrv->r_scope || + p_prout->type != rsrv->r_type || + reservation_key != rsrv->r_key) { + queue_prt(mgmtq, Q_PR_IO, + "PGROUT release failed - transportID=%s\n" + "\tkey:%016x isid:%016x scope:%d type:%d \n", + T10_PGR_TID(cmd), reservation_key, T10_PGR_ISID(cmd), + p_prout->scope, p_prout->type); + + /* + * Scope and key must match to release. + */ + cmd->c_lu->l_status = KEY_ILLEGAL_REQUEST; + cmd->c_lu->l_asc = SPC_ASC_PARAMETERS_CHANGED; + cmd->c_lu->l_ascq = SPC_ASCQ_RES_RELEASED; + status = STATUS_CHECK; + } else { + /* + * Now release the reservation. + */ + queue_prt(mgmtq, Q_PR_IO, + "PGROUT release - transportID=%s\n" + "\tkey:%016x isid:%016x scope:%d type:%d \n", + rsrv->r_transportID, rsrv->r_key, rsrv->r_isid, + rsrv->r_scope, rsrv->r_type); + + spc_pr_rsrv_release(cmd, pgr, rsrv); + status = STATUS_GOOD; + } + + return (status); +} + +/* + * []---- + * | spc_pr_preempt + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/* ARGSUSED */ +static int +spc_pr_preempt(t10_cmd_t *cmd, void *data, size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + t10_lu_impl_t *lu; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + uint64_t reservation_key; + uint64_t service_key; + spc_pr_key_t *key; + spc_pr_rsrv_t *rsrv; + int status = STATUS_GOOD; + + /* + * Get reservation values + */ + reservation_key = SCSI_READ64(plist->reservation_key); + service_key = SCSI_READ64(plist->service_key); + + + /* + * Initiator must be registered and service key (preempt key) + * must exist. + */ + if ((!(key = spc_pr_key_find(pgr, service_key, 0, ""))) || + (!(rsrv = spc_pr_rsrv_find(pgr, reservation_key, + T10_PGR_ISID(cmd), "")))) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: preempt failed reservation:%016x, key:%016x\n", + reservation_key, service_key); + + return (STATUS_RESERVATION_CONFLICT); + } else { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: preempt reservation:%016x, key:%016x\n", + reservation_key, service_key); + } + + /* + * Preempt all keys matching service action key and free + * the associated structures. Do not set UNIT_ATTN for + * the Initiator which requested the action. + * + * Unlike the other Persistent Reservation commands, the preempt, + * preempt_and_abort and clear actions are service delivery port + * independent. So we remove matching keys across ports. + */ + for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + key != (spc_pr_key_t *)&pgr->pgr_keylist; + key = (spc_pr_key_t *)key->k_link.lnk_fwd) { + + /* Skip non-matching keys */ + if (key->k_key != service_key) + continue; + + /* Remove the registration key. */ + spc_pr_key_free(pgr, key); + + /* Do not set UNIT ATTN for calling Initiator */ + if (key->k_isid == T10_PGR_ISID(cmd)) + continue; + + /* + * Find associated I_T Nexuses + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + lu->l_cmd = sbc_cmd; + lu->l_status = KEY_UNIT_ATTENTION; + lu->l_asc = SPC_ASC_PARAMETERS_CHANGED; + lu->l_ascq = SPC_ASCQ_RES_PREEMPTED; + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); + } while (lu != NULL); + + /* + * Is this the preempt and abort? + */ + if (p_prout->action == PR_OUT_PREEMPT_ABORT) { + queue_message_set( + cmd->c_lu->l_common->l_from_transports, + Q_HIGH, msg_reset_lu, (void *)cmd->c_lu); + } + } + + /* + * Re-establish our registration key if we preempted it. + */ + if (!(key = spc_pr_key_find( + pgr, reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)))) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: preempt - register:%016x, isid:%016x:%s\n", + reservation_key, T10_PGR_ISID(cmd), T10_PGR_TID(cmd)); + + key = spc_pr_key_alloc(pgr, reservation_key, + T10_PGR_ISID(cmd), T10_PGR_TID(cmd)); + } + + /* + * Now look for a matching reservation to preempt. + */ + for (rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd; + rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist; + rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd) { + + /* Skip non-matching keys */ + if (rsrv->r_key != service_key) + continue; + + /* + * Remove matching reservations on other ports + * and establish a new reservation on this port only. + * To change the fuctionality to preempt rather than + * delete the reservations on other ports just remove + * the following block of code. + */ + if (strcmp(rsrv->r_transportID, T10_PGR_TID(cmd))) { + spc_pr_rsrv_free(pgr, rsrv); + continue; + } + + rsrv->r_key = reservation_key; + rsrv->r_isid = T10_PGR_ISID(cmd); + rsrv->r_scope = p_prout->scope; + rsrv->r_type = p_prout->type; + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT preempt - transportID=%s\n" + "\tkey:%016x isid:%016x scope:%d type:%d \n", + rsrv->r_transportID, rsrv->r_key, rsrv->r_isid, + rsrv->r_scope, rsrv->r_type); + } + + return (status); +} + +/* + * []---- + * | spc_pr_clear + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +/* ARGSUSED */ +static int +spc_pr_clear(t10_cmd_t *cmd, void *data, size_t data_len) +{ + scsi_cdb_prout_t *p_prout = (scsi_cdb_prout_t *)cmd->c_cdb; + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + scsi_prout_plist_t *plist = (scsi_prout_plist_t *)data; + uint64_t reservation_key; + spc_pr_key_t *key; + t10_targ_impl_t *tp; + t10_lu_impl_t *lu; + + /* + * Do not allow an unregistered initiator to attempting to + * clear the PGR. + */ + reservation_key = SCSI_READ64(plist->reservation_key); + if (!spc_pr_key_find(pgr, reservation_key, T10_PGR_ISID(cmd), "")) { + + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: clear pgr:%016x not found\n", reservation_key); + + return (STATUS_RESERVATION_CONFLICT); + } else { + queue_prt(mgmtq, Q_PR_IO, + "PGROUT: clear pgr:%016x\n", reservation_key); + } + + /* + * We need to set UNIT ATTENTION for all registered initiators. + */ + for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + key != (spc_pr_key_t *)&pgr->pgr_keylist; + key = (spc_pr_key_t *)key->k_link.lnk_fwd) { + + /* Do not set UNIT ATTN for calling Initiator */ + if (key->k_isid == T10_PGR_ISID(cmd)) + continue; + /* + * At this point the only way to get in here is to be the owner + * of the reservation. + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + lu->l_status = KEY_UNIT_ATTENTION; + lu->l_asc = SPC_ASC_PARAMETERS_CHANGED; + lu->l_ascq = SPC_ASCQ_RES_PREEMPTED; + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); + } while (lu != NULL); + } + + /* + * Now erase the reservation and registration info. + */ + spc_pr_erase(pgr); + + return (STATUS_GOOD); +} + +/* + * []---- + * | spc_pr_register_and_move + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static int +spc_pr_register_and_move(t10_cmd_t *cmd, void *data, size_t data_len) +{ + return (STATUS_RESERVATION_CONFLICT); +} + +/* + * []---- + * | spc_pr_key_alloc - + * | Allocate a new registration key and add it to the key list. + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static spc_pr_key_t * +spc_pr_key_alloc(scsi3_pgr_t *pgr, uint64_t service_key, uint64_t isid, + char *transportID) +{ + spc_pr_key_t *key = (spc_pr_key_t *) + memalign(sizeof (void *), sizeof (spc_pr_key_t)); + + if (key != NULL) { + key->k_key = service_key; + key->k_isid = isid; + key->k_transportID = strdup(transportID); + + insque(&key->k_link, pgr->pgr_keylist.lnk_bwd); + + pgr->pgr_numkeys++; + assert(pgr->pgr_numkeys > 0); + } + + return (key); +} + +/* + * []---- + * | spc_pr_key_rsrv_init - + * | Initialize registration & reservervation queues + * []---- + */ +static void +spc_pr_key_rsrv_init(scsi3_pgr_t *pgr) +{ + assert(pgr->pgr_numrsrv == 0); + assert(pgr->pgr_numkeys == 0); + pgr->pgr_rsrvlist.lnk_fwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd; + + assert(pgr->pgr_rsrvlist.lnk_bwd == NULL); + pgr->pgr_rsrvlist.lnk_bwd = (key_link_t *)&pgr->pgr_rsrvlist.lnk_fwd; + + assert(pgr->pgr_keylist.lnk_fwd == NULL); + pgr->pgr_keylist.lnk_fwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd; + + assert(pgr->pgr_keylist.lnk_bwd == NULL); + pgr->pgr_keylist.lnk_bwd = (key_link_t *)&pgr->pgr_keylist.lnk_fwd; +} + +/* + * []---- + * | spc_pr_key_free - + * | Free a registration key + * []---- + */ +static void +spc_pr_key_free(scsi3_pgr_t *pgr, spc_pr_key_t *key) +{ + remque(&key->k_link); + free(key->k_transportID); + free(key); + + pgr->pgr_numkeys--; + assert(pgr->pgr_numkeys >= 0); +} + +/* + * []---- + * | spc_pr_key_find - + * | Find a registration key based on the key, owner id and port id. + * []---- + */ +static spc_pr_key_t * +spc_pr_key_find(scsi3_pgr_t *pgr, uint64_t key, uint64_t isid, + char *transportID) +{ + spc_pr_key_t *kp; + spc_pr_key_t *rval = NULL; + + + for (kp = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + kp != (spc_pr_key_t *)&pgr->pgr_keylist; + kp = (spc_pr_key_t *)kp->k_link.lnk_fwd) { + if ((key == 0 || kp->k_key == key) && + (isid == 0 || kp->k_isid == isid) && + (strlen(transportID) == 0 || + (strcmp(kp->k_transportID, transportID) == 0))) { + rval = kp; + break; + } + } + + return (rval); +} + + +/* + * []---- + * | spc_pr_rsrv_alloc - + * | Allocate a new reservation and add it to the rsrv list. + * []---- + */ +static spc_pr_rsrv_t * +spc_pr_rsrv_alloc(scsi3_pgr_t *pgr, uint64_t service_key, uint64_t isid, + char *transportID, uint8_t scope, uint8_t type) +{ + spc_pr_rsrv_t *rsrv = (spc_pr_rsrv_t *) + memalign(sizeof (void *), sizeof (spc_pr_rsrv_t)); + + if (rsrv != NULL) { + rsrv->r_key = service_key; + rsrv->r_isid = isid; + rsrv->r_transportID = strdup(transportID); + rsrv->r_scope = scope; + rsrv->r_type = type; + + insque(&rsrv->r_link, pgr->pgr_rsrvlist.lnk_bwd); + + pgr->pgr_numrsrv++; + assert(pgr->pgr_numrsrv > 0); + } + + return (rsrv); +} + + +/* + * []---- + * | spc_pr_rsrv_free - + * | Free a reservation. + * []---- + */ +static void +spc_pr_rsrv_free(scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv) +{ + remque(&rsrv->r_link); + free(rsrv->r_transportID); + free(rsrv); + + pgr->pgr_numrsrv--; + assert(pgr->pgr_numrsrv >= 0); +} + +/* + * []---- + * | spc_pr_rsrv_find - + * | Find a reservation based on the key, owner id and port id. + * []---- + */ +static spc_pr_rsrv_t * +spc_pr_rsrv_find(scsi3_pgr_t *pgr, uint64_t key, uint64_t isid, + char *transportID) +{ + spc_pr_rsrv_t *rp, *rval = NULL; + + for (rp = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd; + rp != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist; + rp = (spc_pr_rsrv_t *)rp->r_link.lnk_fwd) { + if ((key == 0 || rp->r_key == key) && + (isid == 0 || rp->r_isid == isid) && + (strlen(transportID) == 0 || + (strcmp(rp->r_transportID, transportID) == 0))) { + rval = rp; + break; + } + } + + return (rval); +} + +/* + * []---- + * | spc_pr_erase - + * | Find specified key / reservation and erease it + * []---- + */ +/* + */ +static void +spc_pr_erase(scsi3_pgr_t *pgr) +{ + spc_pr_key_t *key; + spc_pr_rsrv_t *rsrv; + + while ((key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd) != + (spc_pr_key_t *)&pgr->pgr_keylist) { + spc_pr_key_free(pgr, key); + } + + assert(pgr->pgr_numkeys == 0); + + while ((rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd) != + (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist) { + spc_pr_rsrv_free(pgr, rsrv); + } + + assert(pgr->pgr_numrsrv == 0); + + pgr->pgr_generation = 0; + pgr->pgr_aptpl = 0; +} + +/* + * []---- + * | spc_pr_rsrv_release - + * | Release the reservation the perform any other required clearing actions. + * | Refer to SPC-3, Section 6.1, Tables ?? and ?? + * []---- + */ +static void +spc_pr_rsrv_release(t10_cmd_t *cmd, scsi3_pgr_t *pgr, spc_pr_rsrv_t *rsrv) +{ + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + t10_lu_impl_t *lu; + spc_pr_key_t *key; + + /* + * For Registrants-Only mode set UNIT ATTN. + */ + if (rsrv->r_type == PGR_TYPE_WR_EX_RO || + rsrv->r_type == PGR_TYPE_EX_AC_RO) { + + for (key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + key != (spc_pr_key_t *)&pgr->pgr_keylist; + key = (spc_pr_key_t *)key->k_link.lnk_fwd) { + + /* + * No UNIT ATTN for the requesting Initiator. + */ + if (key->k_isid == T10_PGR_ISID(cmd)) + continue; + + /* + * Find associated I_T Nexuses + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + lu->l_cmd = sbc_cmd; + lu->l_status = KEY_UNIT_ATTENTION; + lu->l_asc = SPC_ASC_PARAMETERS_CHANGED; + lu->l_ascq = SPC_ASCQ_RES_RELEASED; + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, + lu); + } while (lu != NULL); + } + } + + /* + * Remove the reservation. + */ + spc_pr_rsrv_free(pgr, rsrv); +} + +/* + * []---- + * | spc_pr_read - + * | Read in pgr keys and reservations for this device from backend storage. + * | At least the local pgr write lock must be held. + * []---- + */ +Boolean_t +spc_pr_read(t10_cmd_t *cmd) +{ + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_key_t *key; + spc_pr_rsrv_t *rsrv; + spc_pr_diskkey_t *klist; + spc_pr_diskrsrv_t *rlist; + spc_pr_persist_disk_t *buf = NULL; + t10_lu_impl_t *lu; + int i, pfd; + Boolean_t status = False; + char path[MAXPATHLEN]; + + /* + * If the pre-processor supported "#if .. sizeof", these would + * not be required here + */ + assert(sizeof (spc_pr_diskkey_t) == 256); + assert(sizeof (spc_pr_diskrsrv_t) == 256); + assert(sizeof (spc_pr_persist_disk_t) == 512); + + /* + * Open/create the PERSISTANCE file specification + */ + (void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d", + target_basedir, cmd->c_lu->l_targ->s_targ_base, + PERSISTANCEBASE, cmd->c_lu->l_common->l_num); + if ((pfd = open(path, O_RDONLY)) >= 0) { + struct stat pstat; + if ((fstat(pfd, &pstat)) == 0) + if (pstat.st_size > 0) + if (buf = malloc(pstat.st_size)) + if (read(pfd, buf, pstat.st_size) == + pstat.st_size) + status = True; + } + + /* + * Clean up on no persistence file found + */ + if (status == False) { + if (pfd >= 0) + close(pfd); + if (buf) + free(buf); + return (status); + } + + /* + * If this is the first time using the persistance data, + * initialize the reservation and resource key queues + */ + if (pgr->pgr_rsrvlist.lnk_fwd == NULL) { + spc_pr_key_rsrv_init(pgr); + } + + /* + * Perform some vailidation + */ + if ((buf->magic != PGRMAGIC) || + (buf->revision != SPC_PGR_PERSIST_DATA_REVISION)) { + status = False; + goto done; + } + + /* + * Get the registration keys. + */ + klist = buf->keylist; + for (i = 0; i < buf->numkeys; i++) { + if (klist[i].rectype != PGRDISKKEY) { + status = False; + goto done; + } + key = spc_pr_key_alloc(pgr, klist[i].key, klist[i].isid, + klist[i].transportID); + if (key == NULL) { + status = False; + goto done; + } + } + + /* + * Get the reservations. + */ + rlist = (spc_pr_diskrsrv_t *)&buf->keylist[buf->numkeys]; + for (i = 0; i < buf->numrsrv; i++) { + if (rlist[i].rectype != PGRDISKRSRV) { + status = False; + goto done; + } + rsrv = spc_pr_rsrv_alloc(pgr, rlist[i].key, rlist[i].isid, + rlist[i].transportID, rlist[i].scope, rlist[i].type); + if (rsrv == NULL) { + status = False; + goto done; + } + } + + /* + * If there was data then set the reservation type. + */ + if (pgr->pgr_numkeys > 0 || pgr->pgr_numrsrv > 0) { + res->res_type = RT_PGR; + pgr->pgr_generation = buf->generation; + + /* + * Set the command dispatcher according to the reservation type + */ + lu = avl_first(&cmd->c_lu->l_common->l_all_open); + do { + lu->l_cmd = sbc_cmd_reserved; + lu = AVL_NEXT(&cmd->c_lu->l_common->l_all_open, lu); + } while (lu != NULL); + } + +done: pthread_rwlock_unlock(&res->res_rwlock); + free(buf); + return (status); +} + +/* + * []---- + * | spc_pr_write - + * | Write PGR keys and reservations for this device to backend storage. + * | At least the local pgr write lock must be held. + * []---- + */ +Boolean_t +spc_pr_write(t10_cmd_t *cmd) +{ + disk_params_t *p = (disk_params_t *)T10_PARAMS_AREA(cmd); + sbc_reserve_t *res = &p->d_sbc_reserve; + scsi3_pgr_t *pgr = &res->res_scsi_3_pgr; + spc_pr_key_t *key; + spc_pr_rsrv_t *rsrv; + spc_pr_diskkey_t *klist; + spc_pr_diskrsrv_t *rlist; + spc_pr_persist_disk_t *buf; + ssize_t length, bufsize; + int i, pfd = -1; + char path[MAXPATHLEN]; + Boolean_t status = True; + + /* + * If the pre-processor supported "#if .. sizeof", these would + * not be required here + */ + assert(sizeof (spc_pr_diskkey_t) == 256); + assert(sizeof (spc_pr_diskrsrv_t) == 256); + assert(sizeof (spc_pr_persist_disk_t) == 512); + + /* + * Verify space requirements and allocate buffer memory. + * Space needed is header + keylist + rsrvlist. + * Subtract 1 from numkeys since header already defines + * the first element of the keylist. + * Round up the bufsize to the next FBA boundary. + */ + bufsize = sizeof (spc_pr_persist_disk_t) + + (pgr->pgr_numkeys - 1) * sizeof (spc_pr_diskkey_t) + + pgr->pgr_numrsrv * sizeof (spc_pr_diskrsrv_t); + bufsize = roundup(bufsize, 512); + if ((buf = memalign(sizeof (void *), bufsize)) == NULL) + return (False); + else + bzero(buf, bufsize); + + /* + * Build header. + */ + buf->magic = PGRMAGIC; + buf->revision = SPC_PGR_PERSIST_DATA_REVISION; + buf->generation = pgr->pgr_generation; + buf->numkeys = pgr->pgr_numkeys; + buf->numrsrv = pgr->pgr_numrsrv; + + /* + * Copy the keys. + */ + klist = buf->keylist; + for (i = 0, key = (spc_pr_key_t *)pgr->pgr_keylist.lnk_fwd; + key != (spc_pr_key_t *)&pgr->pgr_keylist && i < pgr->pgr_numkeys; + key = (spc_pr_key_t *)key->k_link.lnk_fwd, i++) { + + klist[i].rectype = PGRDISKKEY; + klist[i].reserved = 0; + klist[i].key = key->k_key; + klist[i].isid = key->k_isid; + strncpy(klist[i].transportID, key->k_transportID, + sizeof (klist[i].transportID)); + } + + /* + * Copy the reservations. + */ + rlist = (spc_pr_diskrsrv_t *)&buf->keylist[pgr->pgr_numkeys]; + for (i = 0, rsrv = (spc_pr_rsrv_t *)pgr->pgr_rsrvlist.lnk_fwd; + rsrv != (spc_pr_rsrv_t *)&pgr->pgr_rsrvlist && + i < pgr->pgr_numrsrv; + rsrv = (spc_pr_rsrv_t *)rsrv->r_link.lnk_fwd, i++) { + + rlist[i].rectype = PGRDISKRSRV; + rlist[i].reserved = 0; + rlist[i].scope = rsrv->r_scope; + rlist[i].type = rsrv->r_type; + rlist[i].key = rsrv->r_key; + rlist[i].isid = rsrv->r_isid; + strncpy(rlist[i].transportID, rsrv->r_transportID, + sizeof (rlist[i].transportID)); + } + + /* + * Open/create the PERSISTANCE file specification + */ + (void) snprintf(path, MAXPATHLEN, "%s/%s/%s%d", + target_basedir, cmd->c_lu->l_targ->s_targ_base, + PERSISTANCEBASE, cmd->c_lu->l_common->l_num); + if ((pfd = open(path, O_WRONLY|O_CREAT)) >= 0) { + length = write(pfd, buf, bufsize); + close(pfd); + } else { + if ((pfd < 0) || (length != bufsize)) + status = False; + } + + /* + * Free allocated buffer + */ + free(buf); + return (status); +} diff --git a/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h new file mode 100644 index 0000000000..7ebc085a1b --- /dev/null +++ b/usr/src/cmd/iscsi/iscsitgtd/t10_spc_pr.h @@ -0,0 +1,165 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _T10_SPC_PR_H +#define _T10_SPC_PR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SPC-3 Persistent Reservation specific structures and defines + */ + +/* + * Key Linked Lists + */ +typedef struct key_link { + union { + uint64_t align; + struct { + struct key_link *_lnk_fwd; /* Forward element */ + struct key_link *_lnk_bwd; /* Backward element */ + } key_ptr; + } key_link; +} key_link_t; +#define lnk_fwd key_link.key_ptr._lnk_fwd +#define lnk_bwd key_link.key_ptr._lnk_bwd +#define insque(a, b) do { \ + ((key_link_t *)(a))->lnk_fwd = (key_link_t *)(b); \ + ((key_link_t *)(a))->lnk_bwd = ((key_link_t *)(b))->lnk_bwd; \ + ((key_link_t *)(b))->lnk_bwd = (key_link_t *)(a); \ + ((key_link_t *)(a))->lnk_bwd->lnk_fwd = (key_link_t *)(a); \ + } while (0) + +#define remque(A) do { \ + ((key_link_t *)(A))->lnk_bwd->lnk_fwd = ((key_link_t *)(A))->lnk_fwd; \ + ((key_link_t *)(A))->lnk_fwd->lnk_bwd = ((key_link_t *)(A))->lnk_bwd; \ + } while (0) + +/* + * Reservation Types (res_type). + */ +typedef enum { + RT_NONE = 0, /* None */ + RT_PGR /* SCSI-3 Persistent Reservation */ +} spc_reserve_types; + +/* + * Persistent reservation data. + */ +typedef struct spc_pr_key { + key_link_t k_link; /* Key linked list */ + uint64_t k_key; /* registration key */ + uint64_t k_isid; /* Owner ISID */ + char *k_transportID; /* transport ID */ +} spc_pr_key_t; + +typedef struct spc_pr_rsrv { + key_link_t r_link; /* Key linked list */ + uint64_t r_key; /* reservation key */ + uint64_t r_isid; /* Owner ISID */ + char *r_transportID; /* transport ID */ + uint8_t r_scope; /* reservation scope */ + uint8_t r_type; /* reservation type */ +} spc_pr_rsrv_t; + +/* + * Persistent Reservation data + */ +typedef struct scsi3_pgr { + uint32_t pgr_generation; /* PGR PRgeneration value */ + uint16_t pgr_unused; + uint16_t pgr_bits : 15, + pgr_aptpl : 1; /* persistence data exists */ + int32_t pgr_numkeys; /* # entries in key list */ + int32_t pgr_numrsrv; /* # entries in rsrv list */ + key_link_t pgr_keylist; /* Registration key list */ + key_link_t pgr_rsrvlist; /* reservation list */ +} scsi3_pgr_t; + +typedef struct sbc_reserve { + spc_reserve_types res_type; /* standard or pr active */ + pthread_rwlock_t res_rwlock; /* Lock for coordination */ + scsi3_pgr_t res_scsi_3_pgr; /* SCSI-3 PGR */ +} sbc_reserve_t; + +/* + * On-disk PGR data. + * + * NOTE: The following three structures should be rounded up to 256 bytes each + * to prevent potential problems with on-disk data. + */ +typedef struct spc_pr_diskkey { + uint32_t rectype; /* record type */ + uint32_t reserved; + uint64_t key; /* registration key */ + uint64_t isid; /* Owner ISID */ + char transportID[228]; /* transport ID */ + char filler[4]; /* Unsed, round to 256 bytes */ +} spc_pr_diskkey_t; + +typedef struct spc_pr_diskrsrv { + uint32_t rectype; /* record type */ + uint16_t reserved; + uint8_t scope; /* reservation scope */ + uint8_t type; /* reservation type */ + uint64_t key; /* reservation key */ + uint64_t isid; /* Owner ISID */ + char transportID[228]; /* Transport ID */ + char filler[4]; /* Unsed, round to 256 bytes */ +} spc_pr_diskrsrv_t; + +typedef struct spc_pr_persist_disk { + uint64_t magic; /* magic number */ + uint32_t revision; /* header format revision */ + uint32_t generation; /* pgr generation count */ + int32_t numkeys; /* # items in key list */ + int32_t numrsrv; /* # items in rsrv list */ + char filler[232]; /* Unused, round to 256 bytes */ + +/* + * After the header the data is laid out as follows: + * spc_pr_diskkey_t keylist[]; + * spc_pr_diskrsrv_t rsrvlist[]; + */ + spc_pr_diskkey_t keylist[1]; +} spc_pr_persist_disk_t; + + +#define SPC_PGR_PERSIST_DATA_REVISION 0x01 /* REVISON = 1 */ +#define PGRMAGIC 0x5047524D41474943LL /* "PGRMAGIC" */ +#define PGRDISKKEY 0x5047526B /* "PGRk" */ +#define PGRDISKRSRV 0x50475272 /* "PGRr" */ + +#ifdef __cplusplus +} +#endif + +#endif /* _T10_SPC_PR_H */ diff --git a/usr/src/cmd/iscsi/iscsitgtd/target.h b/usr/src/cmd/iscsi/iscsitgtd/target.h index 2314d44327..9b3dba830e 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/target.h +++ b/usr/src/cmd/iscsi/iscsitgtd/target.h @@ -76,10 +76,18 @@ extern "C" { #define PARAMBASE "params." #define LUNBASE "lun." #define OSDBASE "osd_root." - +#define PERSISTANCEBASE "pgr." #define ISCSI_TARGET_ALIAS "TargetAlias" /* + * Base file name for persistent reservation data (PR). The format used is pr. + * This name is used both to build the PR name and when searching the target + * directory for persistent reservation data. Don't change these names unless + * the upgrade path has been thought about. + */ +#define PRBASE "persistent_reservations" + +/* * The IQN names that are created use libuuid + the local target name * as the idr_str portion of: iqn.1986-03.com.sun:<version>:<id_str> * In case this changes we also include a version number. Currently @@ -201,7 +209,8 @@ extern int main_vers_maj, extern Boolean_t enforce_strict_guid, thin_provisioning, disable_tpgs, - dbg_timestamps; + dbg_timestamps, + pgr_persist; extern pthread_mutex_t targ_config_mutex; extern umem_cache_t *iscsi_cmd_cache, *t10_cmd_cache, diff --git a/usr/src/cmd/iscsi/iscsitgtd/util_queue.c b/usr/src/cmd/iscsi/iscsitgtd/util_queue.c index 3899cc4624..e40ebcbc30 100644 --- a/usr/src/cmd/iscsi/iscsitgtd/util_queue.c +++ b/usr/src/cmd/iscsi/iscsitgtd/util_queue.c @@ -196,8 +196,8 @@ void queue_walker_free(target_queue_t *q, Boolean_t (*func)(msg_t *m, void *v), void *v1) { - msg_t *m, /* current working message */ - *n; /* next message */ + msg_t *m; /* current working message */ + msg_t *n; /* next message */ (void) pthread_mutex_lock(&q->q_mutex); m = q->q_head; @@ -234,8 +234,8 @@ queue_walker_free(target_queue_t *q, Boolean_t (*func)(msg_t *m, void *v), void queue_reset(target_queue_t *q) { - msg_t *m, - *n; + msg_t *m; + msg_t *n; (void) pthread_mutex_lock(&q->q_mutex); m = q->q_head; @@ -301,8 +301,8 @@ queue_message_free(msg_t *m) void queue_free(target_queue_t *q, void (*free_func)(msg_t *)) { - msg_t *m, - *n; + msg_t *m; + msg_t *n; (void) pthread_mutex_lock(&q->q_mutex); m = q->q_head; @@ -343,8 +343,8 @@ queue_str(target_queue_t *q, uint32_t lvl, msg_type_t type, char *fmt) { int len; char *m; - hrtime_t h = gethrtime(), - delta; + hrtime_t h = gethrtime(); + hrtime_t delta; static hrtime_t last_h = 0; (void) pthread_mutex_lock(&q_mutex); diff --git a/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h b/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h index bb5ba7f887..3f97ad1a65 100644 --- a/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h +++ b/usr/src/lib/libiscsitgt/common/iscsitgt_impl.h @@ -145,6 +145,7 @@ typedef enum { #define XML_ELEMENT_VALIDATE "validate" #define XML_ELEMENT_MORESPACE "more-space-required" #define XML_VALUE_TRUE "true" +#define XML_ELEMENT_PGR_PERSIST "PGR-persist" typedef enum { NodeFree, diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index b4591f05d9..a2d180667b 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -788,6 +788,7 @@ SCSIGENHDRS= \ inquiry.h \ message.h \ mode.h \ + persist.h \ sense.h \ status.h diff --git a/usr/src/uts/common/sys/scsi/generic/commands.h b/usr/src/uts/common/sys/scsi/generic/commands.h index 6dffa0495b..02732fe83c 100644 --- a/usr/src/uts/common/sys/scsi/generic/commands.h +++ b/usr/src/uts/common/sys/scsi/generic/commands.h @@ -332,6 +332,11 @@ extern "C" { #define SCMD_GROUP5 0xA0 #define SCMD_REPORT_LUNS 0xA0 #define SCMD_REPORT_TARGET_PORT_GROUPS 0xA3 +#define SCMD_SET_DEVICE 0xA4 +#define SCMD_SET_DEVICE_IDENTIFIER 0x06 +#define SCMD_SET_PRIORITY 0x0e +#define SCMD_SET_TARGET_PORT_GROUPS 0x0a +#define SCMD_SET_TIMESTAMP 0x0f #define SCMD_READ_G5 0xA8 #define SCMD_WRITE_G5 0xAA #define SCMD_READ_MEDIA_SERIAL 0xAB diff --git a/usr/src/uts/common/sys/scsi/generic/persist.h b/usr/src/uts/common/sys/scsi/generic/persist.h new file mode 100644 index 0000000000..613c91cf12 --- /dev/null +++ b/usr/src/uts/common/sys/scsi/generic/persist.h @@ -0,0 +1,398 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_SCSI_GENERIC_PERSIST_H +#define _SYS_SCSI_GENERIC_PERSIST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SCSI Persistence Data + * + * Format of data returned as a result of PERSISTENCE RESERVER { IN | OUT } + */ + +/* + * SPC-3 revision 23, Section 6.11.1, Table 102 + * Persistent Reservations + * Persistent Reserve In service actions + */ +#define PR_IN_READ_KEYS 0x0 /* Read all registered reservation keys */ +#define PR_IN_READ_RESERVATION 0x1 /* Reads th persistent reservations */ +#define PR_IN_REPORT_CAPABILITIES 0x2 /* Returns capability information */ +#define PR_IN_READ_FULL_STATUS 0x3 /* Reads complete information about all */ + /* registrations and the persistent */ + /* reservations, if any */ +/* + * SPC-3 revision 23, Section 6.11.3.3, Table 106 + * Persistent reservation scope codes + */ +#define PR_LU_SCOPE 0x0 /* Persistent reservation applies to */ + /* full logical unit */ +/* + * SPC-3 revision 23, Section 6.11.3.4, Table 107 + * Persistent Reservations + * Persistent reservation type codes + */ +#define PGR_TYPE_WR_EX 0x1 /* Write Exclusive */ +#define PGR_TYPE_EX_AC 0x3 /* Exclusive Access */ +#define PGR_TYPE_WR_EX_RO 0x5 /* Write Exclusive, Registrants Only */ +#define PGR_TYPE_EX_AC_RO 0x6 /* Exclusive Access, Registrants Only */ +#define PGR_TYPE_WR_EX_AR 0x7 /* Write Exclusive, All Registrants */ +#define PGR_TYPE_EX_AC_AR 0x8 /* Exclusive Access, All Registrants */ + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.5 PERSISTENCE RESERVE IN + * Table 111 - full status descriptor format + */ +/* Table 289 - iSCSI Initiator Device TransportID format */ + +#define iSCSI_PROTOCOL_ID 0x5 /* Table 262 - iSCSI Protocol ID */ +#define WW_UID_DEVICE_NAME 0x0 /* Table 288 - iSCSI Transport IDs */ + + +#if defined(_BIT_FIELDS_LTOH) +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.1 PERSISTENCE RESERVE IN + * Table 101 - PERSISTENCE RESERVE IN command + */ +typedef struct scsi_cdb_prin { + uint8_t cmd; + uint8_t action : 5, + resbits : 3; + uint8_t resbytes[5]; + uint8_t alloc_len[2]; + uint8_t control; +} scsi_cdb_prin_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.2 PERSISTENCE RESERVE IN + * Table 103/104/105 - parameter data for READS KEYS + */ +typedef struct scsi_prin_rsrvdesc { + uint8_t reservation_key[8]; + uint8_t obsolete1[4]; + uint8_t resbytes; + uint8_t type : 4, + scope : 4; + uint8_t obsolete2[2]; +} scsi_prin_rsrvdesc_t; +typedef struct scsi_prin_readrsrv { + uint8_t PRgeneration[4]; + uint8_t add_len[4]; + scsi_prin_rsrvdesc_t res_key_list[1]; +} scsi_prin_readrsrv_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.4 PERSISTENCE RESERVE IN + * Table 108 - parameter data for REPORT CAPABILTIES + */ +typedef struct scsi_per_res_type { + uint8_t resbits1 : 1, + wr_ex : 1, + resbits2 : 1, + ex_ac : 1, + resbits3 : 1, + wr_ex_ro : 1, + ex_ac_ro : 1, + wr_ex_ar : 1; + uint8_t ex_ac_ar : 1, + resbits4 : 7; +} scsi_per_res_type_t; +typedef struct scsi_prin_rpt_cap { + uint8_t length[2]; + uint8_t ptpl_c : 1, + resbits1 : 1, + atp_c : 1, + sip_c : 1, + crh : 1, + resbits2 : 3; + uint8_t ptpl_a : 1, + resbits3 : 6, + tmv : 1; + scsi_per_res_type_t pr_type; + uint8_t resbytes[2]; +} scsi_prin_rpt_cap_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.5 PERSISTENCE RESERVE IN + * Table 110/111 - parameter data for READ FULL STATUS + * Table 281 - TransportId format + */ +typedef struct scsi_transport_id { + uint8_t protocol_id : 4, + resbits : 2, + format_code : 2; + uint8_t add_len[2]; + char iscsi_name[1]; +} scsi_transport_id_t; +typedef struct scsi_prin_status_t { + uint8_t reservation_key[8]; + uint8_t resbytes1[4]; + uint8_t r_holder : 1, + all_tg_pt : 1, + resbits : 6; + uint8_t type : 4, + scope : 4; + uint8_t resbytes2[4]; + uint8_t rel_tgt_port_id[2]; + uint8_t add_len[4]; + scsi_transport_id_t trans_id; +} scsi_prin_status_t; +typedef struct scsi_prin_full_status { + uint8_t PRgeneration[4]; + uint8_t add_len[4]; + scsi_prin_status_t full_desc[1]; +} scsi_prin_full_status_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12.1 PERSISTENCE RESERVE OUT + * Table 112 - PERSISTENCE RESERVE OUT command + */ +typedef struct scsi_cdb_prout { + uint8_t cmd; + uint8_t action : 5, + resbits : 3; + uint8_t type : 4, + scope : 4; + uint8_t resbytes[2]; + uint8_t param_len[4]; + uint8_t control; +} scsi_cdb_prout_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12.3 PERSISTENCE RESERVE OUT + * Table 114 - PERSISTENCE RESERVE OUT parameter list + */ +typedef struct scsi_prout_plist { + uint8_t reservation_key[8]; + uint8_t service_key[8]; + uint8_t obsolete1[4]; + uint8_t aptpl : 1, + resbits1 : 1, + all_tg_pt : 1, + spec_i_pt : 1, + resbits2 : 4; + uint8_t resbytes1; + uint8_t obsolete2[2]; + uint8_t apd[1]; +} scsi_prout_plist_t; + +#elif defined(_BIT_FIELDS_HTOL) +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.1 PERSISTENCE RESERVE IN + * Table 101 - PERSISTENCE RESERVE IN command + */ +typedef struct scsi_cdb_prin { + uint8_t cmd; + uint8_t resbits : 3, + action : 5; + uint8_t resbytes[5]; + uint8_t alloc_len[2]; + uint8_t control; +} scsi_cdb_prin_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.2 PERSISTENCE RESERVE IN + * Table 103/104/105 - parameter data for READS KEYS + */ +typedef struct scsi_prin_rsrvdesc { + uint8_t reservation_key[8]; + uint8_t obsolete1[4]; + uint8_t resbytes; + uint8_t scope : 4, + type : 4; + uint8_t obsolete2[8]; +} scsi_prin_rsrvdesc_t; +typedef struct scsi_prin_readrsrv { + uint8_t PRgeneration[4]; + uint8_t add_len[4]; + scsi_prin_rsrvdesc_t res_key_list[1]; +} scsi_prin_readrsrv_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.4 PERSISTENCE RESERVE IN + * Table 108 - parameter data for REPORT CAPABILTIES + */ +typedef struct scsi_per_res_type { + uint8_t wr_ex_ar : 1, + ex_ac_ro : 1, + wr_ex_ro : 1, + resbits3 : 1, + ex_ac : 1, + resbits2 : 1, + wr_ex : 1, + resbits1 : 1; + uint8_t resbits4 : 7, + ex_ac_ar : 1; +} scsi_per_res_type_t; +typedef struct scsi_prin_rpt_cap { + uint8_t length[2]; + uint8_t resbits2 : 3, + crh : 1, + sip_c : 1, + atp_c : 1, + resbits1 : 1, + ptpl_c : 1; + uint8_t tmv : 1, + resbits3 : 6, + ptpl_a : 1; + scsi_per_res_type_t pr_type; + uint8_t resbytes[2]; +} scsi_prin_rpt_cap_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.11.5 PERSISTENCE RESERVE IN + * Table 110/111 - parameter data for READ FULL STATUS + * Table 281 - TransportId format + */ +typedef struct scsi_transport_id { + uint8_t format_code : 2, + resbits : 2, + protocol_id : 4; + uint8_t add_len[2]; + char iscsi_name[1]; +} scsi_transport_id_t; +typedef struct scsi_prin_status_t { + uint8_t reservation_key[8]; + uint8_t resbytes1[4]; + uint8_t resbits : 6, + all_tg_pt : 1, + r_holder : 1; + uint8_t scope : 4, + type : 4; + uint8_t resbytes2[4]; + uint8_t rel_tgt_port_id[2]; + uint8_t add_len[4]; + scsi_transport_id_t trans_id; +} scsi_prin_status_t; +typedef struct scsi_prin_full_status { + uint8_t PRgeneration[4]; + uint8_t add_len[4]; + scsi_prin_status_t full_desc[1]; +} scsi_prin_full_status_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12.1 PERSISTENCE RESERVE OUT + * Table 112 - PERSISTENCE RESERVE OUT command + */ +typedef struct scsi_cdb_prout { + uint8_t cmd; + uint8_t resbits : 3, + action : 5; + uint8_t scope : 4, + type : 4; + uint8_t resbytes[2]; + uint8_t param_len[4]; + uint8_t control; +} scsi_cdb_prout_t; + +/* + * Information obtained from: + * SPC-3, Revision 23 + * Section 6.12.3 PERSISTENCE RESERVE OUT + * Table 114 - PERSISTENCE RESERVE OUT parameter list + */ +typedef struct scsi_prout_plist { + uint8_t reservation_key[8]; + uint8_t service_key[8]; + uint8_t obsolete1[4]; + uint8_t resbits1 : 4, + spec_i_pt : 1, + all_tg_pt : 1, + resbits2 : 1, + aptpl : 1; + uint8_t resbytes1; + uint8_t obsolete2[2]; + uint8_t apd[1]; +} scsi_prout_plist_t; + +#else +#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined +#endif /* _BIT_FIELDS_LTOH */ + + +/* + * SPC-3 revision 23, Section 6.12.2, Table 113 + * Persistent Reservations + * Persistent Reserve Out service action codes + */ +#define PR_OUT_REGISTER 0x0 /* Register/unregister a reservation */ + /* key with the device server */ +#define PR_OUT_RESERVE 0x1 /* Create a persistent reservation */ + /* having a specified SCOPE & TYPE */ +#define PR_OUT_RELEASE 0x2 /* Release the selected persistent */ + /* reservation */ +#define PR_OUT_CLEAR 0x3 /* Clears all reservation keys and */ + /* all persistent reservations */ +#define PR_OUT_PREEMPT 0x4 /* Preempts persistent reservations */ + /* and/or removes reservations */ +#define PR_OUT_PREEMPT_ABORT 0x5 /* Preempts persistent reservations */ + /* and/or removes reservations, and */ + /* aborts all tasks for all preempted */ + /* I_T nexuses */ +#define PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY 0x06 + /* Register a reservation key with */ + /* the device server, or unregister a */ + /* reservation key */ +#define PR_OUT_REGISTER_MOVE 0x7 /* Register a reservation key for */ + /* another I_T nexus with the device */ + /* server and move a persistent */ + /* reservation to the I_T nexus */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_SCSI_GENERIC_PERSIST_H */ |