diff --git a/osd-target/tracking.c b/osd-target/tracking.c
new file mode 100644
index 0000000..d59dfbf
--- /dev/null
+++ b/osd-target/tracking.c
@@ -0,0 +1,256 @@
+/*
+ * OSD Command Tracking
+ *
+ * Copyright (C) 2011 University of Connecticut. All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/* Writing command tracking info to the attribute database may be
+ expensive. So, just keep an array of command tracking pages in
+ memory in a linked list. When the command is complete, we will
+ flush the attributes to the database. The problem is that if the
+ OSD target crashes before we flush, we can't return a meaningful
+ SAM-4 error to initiator. All we can do is return the previous
+ command call or that "no command is using the collection." */
+
+#include
+#include
+#include
+#include
+
+#include "tracking.h"
+#include "attr.h"
+#include "obj.h"
+#include "list-entry.h"
+#include "osd-util/osd-util.h"
+
+#define NUM_CTPS 10
+struct ctp_node {
+ struct ctp ctp;
+ struct ctp_node *next;
+};
+
+struct ctp_node *ctp_head = NULL, *ctp_tail = NULL;
+int num_ctps = 0;
+
+struct ctp *init_ctp(uint64_t pid, uint64_t cid, uint16_t service_action)
+{
+ struct ctp_node *ctpn = NULL;
+ struct ctp *ctp;
+
+ if (num_ctps == NUM_CTPS) {
+ /* find a ctp that has completed. if it has
+ completed, it has been flushed to the database, we
+ can remove from queue and reuse it */
+ struct ctp_node **ctpnp;
+ for (ctpnp = &ctp_head; *ctpnp; ctpnp = &((*ctpnp)->next)) {
+ ctpn = *ctpnp;
+ if (ctpn->ctp.status != 0xFFFF) {
+ *ctpnp = ctpn->next;
+ break;
+ }
+ }
+ if (ctpn == NULL) {
+ return NULL;
+ }
+ } else {
+ ctpn = (struct ctp_node *)malloc(sizeof(*ctpn));
+ if (num_ctps == 0) {
+ ctp_head = ctp_tail = ctpn;
+ }
+ num_ctps++;
+ }
+
+ if (ctp_tail)
+ ctp_tail->next = ctpn;
+ ctpn->next = NULL;
+ ctp_tail = ctpn;
+
+ ctp = &ctpn->ctp;
+
+ ctp->pid = pid;
+ ctp->cid = cid;
+ ctp->service_action = service_action;
+ ctp->status = 0xFFFF;
+ ctp->percent_complete = 0;
+
+ return ctp;
+}
+
+/* finish_ctp is called when the command completes. The caller should
+ set ctp->status and ctp->sense before calling finish_ctp. The ctp
+ struct will no longer be valid after this call */
+
+void finish_ctp(struct osd_device *osd, struct ctp *ctp)
+{
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_PERCENT_COMPLETE, &ctp->percent_complete,
+ sizeof(ctp->percent_complete));
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_ENDED_COMMAND_STATUS, &ctp->status,
+ sizeof(ctp->status));
+ if (ctp->status) {
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_SENSE_DATA, ctp->sense, ctp->senselen);
+ }
+ if (ctp->number_of_members) {
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_NUMBER_OF_MEMBERS, &ctp->number_of_members,
+ sizeof(ctp->number_of_members));
+ }
+ if (ctp->objects_processed) {
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_OBJECTS_PROCESSED, &ctp->objects_processed,
+ sizeof(ctp->objects_processed));
+ }
+ if (ctp->newer_objects_skipped) {
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_NEWER_OBJECTS_SKIPPED,
+ &ctp->newer_objects_skipped,
+ sizeof(ctp->newer_objects_skipped));
+ }
+ if (ctp->missing_objects_skipped) {
+ attr_set_attr(osd->dbc, ctp->pid, ctp->cid, COLL_TRACKING_PG,
+ CTP_MISSING_OBJECTS_SKIPPED,
+ &ctp->missing_objects_skipped,
+ sizeof(ctp->missing_objects_skipped));
+ }
+ return;
+}
+
+struct ctp *find_ctp(uint64_t pid, uint64_t cid)
+{
+ struct ctp_node *ctpn;
+ for (ctpn = ctp_head; ctpn; ctpn = ctpn->next) {
+ if (ctpn->ctp.pid == pid && ctpn->ctp.cid == cid) {
+ return &ctpn->ctp;
+ }
+ }
+ return NULL;
+}
+
+int get_ctp(struct osd_device *osd, uint64_t pid, uint64_t cid,
+ uint32_t number, void *outbuf, uint64_t outlen,
+ uint8_t listfmt, uint32_t *used_outlen)
+{
+ int ret = 0;
+ const void *val = NULL;
+ uint16_t len = 0;
+ char name[ATTR_PAGE_ID_LEN];
+ char path[MAXNAMELEN];
+ uint8_t ll[8];
+ uint64_t pcount;
+ uint32_t page = COLL_TRACKING_PG;
+
+ struct ctp* ctp = find_ctp(pid, cid);
+
+ switch (number) {
+ case 0:
+ /*{COLL_PG + 4, 0, "INCITS T10 Command Tracking"},*/
+ len = ATTR_PAGE_ID_LEN;
+ sprintf(name, "INCITS T10 Command Tracking");
+ val = name;
+ break;
+ case CTP_PERCENT_COMPLETE:
+ if (ctp) {
+ len = 1;
+ val = &ctp->percent_complete;
+ }
+ break;
+
+ case CTP_ACTIVE_COMMAND_STATUS:
+ if (ctp && ctp->status == 0xFFFF) {
+ set_htons(ll, ctp->service_action);
+ } else {
+ set_htons(ll, 0);
+ }
+ len = 2;
+ val = ll;
+ break;
+
+ case CTP_ENDED_COMMAND_STATUS:
+ if (ctp) {
+ set_htons(ll, ctp->status);
+ len = 2;
+ val = ll;
+ }
+ break;
+
+ case CTP_SENSE_DATA:
+ if (ctp) {
+ len = ctp->senselen;
+ val = ctp->sense;
+ }
+ break;
+
+ case CTP_NUMBER_OF_MEMBERS:
+ if (ctp) {
+ set_htonll(ll, ctp->number_of_members);
+ len = 8;
+ val = ll;
+ }
+ break;
+
+ case CTP_OBJECTS_PROCESSED:
+ if (ctp) {
+ set_htonll(ll, ctp->objects_processed);
+ len = 8;
+ val = ll;
+ }
+ break;
+
+ case CTP_NEWER_OBJECTS_SKIPPED:
+ if (ctp) {
+ set_htonll(ll, ctp->newer_objects_skipped);
+ len = 8;
+ val = ll;
+ }
+ break;
+
+ case CTP_MISSING_OBJECTS_SKIPPED:
+ if (ctp) {
+ set_htonll(ll, ctp->missing_objects_skipped);
+ len = 8;
+ val = ll;
+ }
+ break;
+
+ default:
+ return OSD_ERROR;
+ }
+
+ if (val==NULL) {
+ return attr_get_attr(osd->dbc, pid, cid, page, number,
+ outlen, outbuf, listfmt, used_outlen);
+ }
+
+ if (listfmt == RTRVD_SET_ATTR_LIST)
+ ret = le_pack_attr(outbuf, outlen, page, number, len, val);
+ else if (listfmt == RTRVD_MULTIOBJ_LIST)
+ ret = le_multiobj_pack_attr(outbuf, outlen, cid, page, number,
+ len, val);
+ else
+ return OSD_ERROR;
+
+ assert(ret == -EINVAL || ret == -EOVERFLOW || ret > 0);
+ if (ret == -EOVERFLOW)
+ *used_outlen = 0;
+ else if (ret > 0)
+ *used_outlen = ret;
+ else
+ return ret;
+
+ return OSD_OK;
+}
+
diff --git a/osd-target/tracking.h b/osd-target/tracking.h
new file mode 100644
index 0000000..1ce4cc8
--- /dev/null
+++ b/osd-target/tracking.h
@@ -0,0 +1,54 @@
+/*
+ * OSD Command Tracking
+ *
+ * Copyright (C) 2011 University of Connecticut. All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include "osd-util/osd-defs.h"
+#include "osd-types.h"
+
+struct ctp {
+ uint64_t pid;
+ uint64_t cid;
+ uint16_t service_action;
+ uint16_t status;
+ uint8_t percent_complete;
+ uint8_t senselen;
+ uint8_t sense[OSD_MAX_SENSE];
+ uint64_t number_of_members;
+ uint64_t objects_processed;
+ uint64_t newer_objects_skipped;
+ uint64_t missing_objects_skipped;
+};
+
+/* init_ctp creates a command tracking page for the specified collection */
+
+struct ctp *init_ctp(uint64_t pid, uint64_t cid, uint16_t service_action);
+
+/* finish_ctp is called when the command completes. The caller should
+ set ctp->status and ctp->sense before calling finish_ctp. The ctp
+ struct will no longer be valid after this call */
+
+void finish_ctp(struct osd_device *osd, struct ctp *ctp);
+
+/* find_ctp returns a command tracking page in ctp struct format */
+struct ctp *find_ctp(uint64_t pid, uint64_t cid);
+
+/* get_ctp returns a command tracking page in attribute format */
+int get_ctp(struct osd_device *osd, uint64_t pid, uint64_t oid,
+ uint32_t number, void *outbuf, uint64_t outlen,
+ uint8_t listfmt, uint32_t *used_outlen);
+
+