Exemple #1
0
/* NOTE:  A copy of this code exists in Sparc/memory.s; if you change 
 * anything here, check that code as well.
 */
int stk_restore_frame( word *globals )
{
  word *stktop, *hframe, *p;
  word retoffs, proc, codeaddr, codeptr, header;
  unsigned size;

  assert2(globals[ G_STKP ] == globals[ G_STKBOT ]);

  hframe = ptrof( globals[ G_CONT ] );
  size = roundup8( sizefield( *hframe ) + 4 );   /* bytes to copy */
  stktop = (word*)globals[ G_STKP ];

  stktop -= size / 4;
  if (stktop < (word*)globals[ G_ETOP ]) {
    supremely_annoyingmsg( "Failed to create stack." );
    return 0;
  }
  globals[ G_STKP ] = (word)stktop;
  globals[ G_STKUFLOW ] += 1;

#if 0
  annoyingmsg("Restore: %d", size);
#endif

  /* copy the frame onto the stack */
  p = stktop;
  while (size) {
    *p++ = *hframe++;
    *p++ = *hframe++;
    size -= 8;
  }

  /* Follow continuation chain. */
  globals[ G_CONT ] = *(stktop+STK_DYNLINK);

  header  = *(stktop+HC_HEADER);
  retoffs = *(stktop+HC_RETOFFSET);
  proc    = *(stktop+HC_PROC);

  /* convert the header back to a fixnum */
  *(stktop+STK_CONTSIZE) = sizefield(header);

  /* convert the return address */
  if (proc != 0) {
    codeptr = *(ptrof( proc )+PROC_CODEPTR);
    if (tagof( codeptr ) == BVEC_TAG) {
      codeaddr = (word)ptrof( codeptr );
      *(stktop+STK_RETADDR) = (codeaddr+4)+retoffs;
    } else {
      *(stktop+STK_RETADDR) = retoffs;
    }
  } else {
    *(stktop+STK_RETADDR) = retoffs;
  }

  return 1;
}
static void test_le(uint32_t page, uint32_t num, uint16_t len, 
		    const void *val, uint8_t *cp)
{
	uint8_t pad = 0;

	assert(get_ntohl(cp) == page), cp += 4;
	assert(get_ntohl(cp) == num), cp += 4;
	assert(get_ntohs(cp) == len), cp += 2;
	assert(memcmp(cp, val, len) == 0), cp += len;
	pad = roundup8(10+len) - (10+len);
	while (pad--)
		assert(*cp == 0), cp++;
}
Exemple #3
0
int stk_size_for_top_stack_frame( word *globals )
{
#if OLD_GC_CODE
  return
    nativeint( *(word*)globals[ G_STKP ] )*sizeof( word ) + STACK_BASE_SIZE;
#else
  int frame_size;
  if (globals[ G_STKP ] == globals[ G_STKBOT])
    frame_size = sizefield( *ptrof( globals[ G_CONT ] ) );
  else
    frame_size = *((word*)globals[ G_STKP ] + STK_CONTSIZE);
  return roundup8( frame_size + 4 ) + STACK_BASE_SIZE;
#endif

}
Exemple #4
0
static void* visit_measuring_float( word *addr, int tag, void *accum ) 
{
  struct visit_measuring_float_data *data = 
    (struct visit_measuring_float_data*)accum;
  word obj; 
  bool marked;
  bool marked_via_remsets;
  int words;
  struct float_counts *type_counts;
  obj = tagptr( addr, tag );
  marked = 
    msfloat_object_marked_p( data->context, obj );
  marked_via_remsets = 
    msfloat_object_marked_p( data->context_incl_remsets, obj );

  data->objs.total += 1 ;
  if (!marked && !marked_via_remsets) {
    data->objs.zzflt += 1;
  }
  if (!marked && marked_via_remsets) {
    data->objs.rsflt += 1;
  }

  switch (tag) {
  case PAIR_TAG:
    words = 2; 
    break;
  case VEC_TAG:
  case BVEC_TAG:
  case PROC_TAG:
    words = roundup8( sizefield( *addr )+4 ) / 4;
    break;
  default:
    assert(0);
  }
  data->words.total += words;
  if (!marked && !marked_via_remsets)
    data->words.zzflt += words;
  if (!marked && marked_via_remsets)
    data->words.rsflt += words;
  return data;
}
Exemple #5
0
void stk_flush( word *globals )
{
  word *stktop, *stkbot, *first, *prev;
  word retaddr, codeaddr, codeptr, proc, size;
  unsigned framecount;

  assert2( tagof( globals[ G_REG0 ]) == PROC_TAG );

  stktop = (word*)globals[ G_STKP ];
  stkbot = (word*)globals[ G_STKBOT ];

  stack_state.words_flushed += (stkbot-stktop);
  first = prev = 0;  
  framecount = 0;
  while (stktop < stkbot) {
    size = *(stktop+STK_CONTSIZE);
    retaddr = *(stktop+STK_RETADDR);

    /* convert header to vector header */
    assert2( size % 4 == 0 );	  /* size must be words, a fixnum */
    assert2( (s_word)size >= 12 ); /* 3-word minimum, and nonnegative */
    *(stktop+HC_HEADER) = mkheader( size, VEC_HDR );

    /* convert return address */
    proc = *(stktop+STK_REG0);
    if (proc != 0) {
      assert2( tagof( proc ) == PROC_TAG );
      codeptr = *(ptrof( proc )+PROC_CODEPTR);
      if (tagof( codeptr ) == BVEC_TAG) {
        codeaddr = (word)ptrof( codeptr );
        *(stktop+HC_RETOFFSET) = retaddr-(codeaddr+4);
      } else {
	*(stktop+HC_RETOFFSET) = retaddr;
      }
    } else {
      *(stktop+HC_RETOFFSET) = retaddr;
    }

    /* chain things together */
    if (first == 0)
      first = stktop;
    else
      *(prev+HC_DYNLINK) = (word)tagptr( stktop, VEC_TAG );
    prev = stktop;

    framecount++;

    size = roundup8( size+4 );
    stktop += size / 4;

#if 0
    annoyingmsg("Flush: %d", size );
#endif
  }
  if (prev != 0)
    *(prev+HC_DYNLINK) = globals[ G_CONT ];
  if (first != 0)
    globals[ G_CONT ] = (word)tagptr( first, VEC_TAG );

  globals[ G_STKBOT ] = globals[ G_STKP ];

  stack_state.frames_flushed += framecount;
}
Exemple #6
0
/*
 * returns list of objects along with requested attributes
 *
 * return values:
 * -EINVAL: invalid argument
 * -EIO: prepare or some other sqlite function failed
 * OSD_ERROR: some other error
 * OSD_OK: success
 */
int mtq_list_oids_attr(struct db_context *dbc, uint64_t pid,
		       uint64_t initial_oid, struct getattr_list *get_attr,
		       uint64_t alloc_len, void *outdata, 
		       uint64_t *used_outlen, uint64_t *add_len, 
		       uint64_t *cont_id)
{
	int ret = 0;
	char *cp = NULL;
	char *SQL = NULL;
	uint32_t i = 0;
	uint32_t factor = 2; /* this query uses space fast */
	uint32_t attr_list_len = 0; /*XXX:SD see below */
	uint32_t sqlen = 0;
	uint64_t oid = 0;
	uint32_t page;
	uint32_t number;
	uint16_t len;
	const void *val = NULL;
	sqlite3_stmt *stmt = NULL;
	uint8_t *head = NULL, *tail = NULL;
	const char *select_stmt = NULL;
	const char *obj = obj_getname(dbc);
	const char *attr = attr_getname(dbc);

	assert(dbc && dbc->db && get_attr && outdata && used_outlen 
	       && add_len && obj && attr);

	if (get_attr->sz == 0) {
		ret = -EINVAL;
		goto out;
	}

	SQL = Malloc(MAXSQLEN*factor);
	if (!SQL) {
		ret = -ENOMEM;
		goto out;
	}

	/* 
	 * For each attribute requested, create a select statement,
	 * which will try to index into the attr table with a full key rather
	 * than just (pid, oid) prefix key. Analogous to loop unrolling, we
	 * unroll each requested attribute into its own select statement.
	 */
	select_stmt = "SELECT obj.oid as myoid, attr.page, "
		" attr.number, attr.value FROM %s as obj, %s as attr "
		" WHERE obj.pid = attr.pid AND obj.oid = attr.oid AND "
		" obj.pid = %llu AND obj.type = %u AND "
		" attr.page = %u AND attr.number = %u AND obj.oid >= %llu ";

	cp = SQL;
	sqlen = 0;
	sprintf(SQL, select_stmt, obj, attr, llu(pid), USEROBJECT,
		USER_TMSTMP_PG, 0, llu(initial_oid));
	SQL = strcat(SQL, " UNION ALL ");
	sqlen += strlen(SQL);
	cp += sqlen;
	for (i = 0; i < get_attr->sz; i++) {
		sprintf(cp, select_stmt, obj, attr, llu(pid), USEROBJECT,
			get_attr->le[i].page, get_attr->le[i].number,
			llu(initial_oid));
		if (i < (get_attr->sz - 1))
			cp = strcat(cp, " UNION ALL ");

		sqlen += strlen(cp);
		if (sqlen > (MAXSQLEN*factor - 400)) {
			factor *= 2;
			SQL = realloc(SQL, MAXSQLEN*factor);
			if (!SQL) {
				ret = -ENOMEM;
				goto out;
			}
		}

		cp = SQL + sqlen;
	}
	sprintf(cp, " ORDER BY myoid; "); 

	ret = sqlite3_prepare(dbc->db, SQL, strlen(SQL)+1, &stmt, NULL);
	if (ret != SQLITE_OK) {
		error_sql(dbc->db, "%s: sqlite3_prepare", __func__);
		goto out;
	}

	/* execute the statement */
	head = tail = outdata;
	attr_list_len = 0;
	while(1) {
		ret = sqlite3_step(stmt);
		if (ret == SQLITE_BUSY) {
			continue;
		} else if (ret != SQLITE_ROW) {
			break;
		} 
		/* for the rest of loop body ret == SQLITE_ROW */

		/*
		 * XXX:SD The spec is inconsistent in applying padding and
		 * alignment rules. Here we make changes to the spec. In our
		 * case object descriptor format header (table 79) is 16B
		 * instead of 12B, and attributes list length field is 4B
		 * instead of 2B as defined in spec, and starts at byte 12
		 * in the header (not 10).  ODE is object descriptor entry.
		 */
		oid = sqlite3_column_int64(stmt, 0);
		page = sqlite3_column_int64(stmt, 1);
		number = sqlite3_column_int64(stmt, 2);
		len = sqlite3_column_bytes(stmt, 3);

		if (page == USER_TMSTMP_PG && number == 0) {
			/* look ahead in the buf to see if there is space */
			if (alloc_len >= 16) {
				/* start attr list of 'this' ODE */
				set_htonll(tail, oid);
				memset(tail + 8, 0, 4);  /* reserved */
				if (head != tail) {
					/* fill attr_list_len of prev ODE */
					set_htonl(head, attr_list_len);
					head = tail;
					attr_list_len = 0;
				}
				alloc_len -= 16;
				tail += 16;
				head += 12;  /* points to attr-list-len */
				*used_outlen += 16;
			} else {
				if (head != tail) {
					/* fill attr_list_len of prev ODE */
					set_htonl(head, attr_list_len);
					head = tail;
					attr_list_len = 0;
				}
				if (*cont_id == 0)
					*cont_id = oid;
			}
			/* handle overflow: osd2r01 Sec 6.14.2 */
			if (*add_len + 16 > *add_len) {
				*add_len += 16;
			} else {
				/* terminate since add_len overflew */
				*add_len = (uint64_t) -1;
				ret = SQLITE_DONE; 
				break;
			}
			continue;
		} 
		if (alloc_len >= 16) {
			/* osd_debug("%s: oid %llu, page %u, number %u, len %u",
				  __func__, llu(oid), page, number, len); */
			val = sqlite3_column_blob(stmt, 3);
			ret = le_pack_attr(tail, alloc_len, page, number, len, 
					   val);
			assert (ret != -EOVERFLOW);
			if (ret > 0) {
				alloc_len -= ret;
				tail += ret;
				attr_list_len += ret;
				*used_outlen += ret;
				if (alloc_len < 16){
					set_htonl(head, attr_list_len);
					head = tail;
					attr_list_len = 0;
					if (*cont_id == 0)
						*cont_id = oid;
				}
			} else {
				goto out_finalize;
			}
		} else {
			if (head != tail) {
				/* fill attr_list_len of this ODE */
				set_htonl(head, attr_list_len);
				head = tail;
				attr_list_len = 0;
				if (*cont_id == 0)
					*cont_id = oid;
			}
		}
		/* handle overflow: osd2r01 Sec 6.14.2 */
		if ((*add_len + roundup8(4+4+2+len)) > *add_len) {
			*add_len += roundup8(4+4+2+len);
		} else {
			/* terminate since add_len overflew */
			*add_len = (uint64_t) -1; 
			ret = SQLITE_DONE; 
			break;
		}
	}
	if (ret != SQLITE_DONE) {
		error_sql(dbc->db, "%s: query execution failed. SQL %s, "
			  " add_len %llu attr_list_len %u", __func__, SQL, 
			  llu(*add_len), attr_list_len);
		goto out_finalize;
	}
	if (head != tail) {
		set_htonl(head, attr_list_len);
		head += (4 + attr_list_len);
		assert(head == tail);
	}

	ret = OSD_OK; /* success */

out_finalize:
	if (sqlite3_finalize(stmt) != SQLITE_OK) {
		ret = -EIO;
		error_sql(dbc->db, "%s: finalize", __func__);
	}

out:
	free(SQL);
	return ret;
}
static void time_attr(struct osd_device *osd, int numpg, int numattr, 
		      int numiter, int test, const char *func)
{
	int ret = 0;
	int i = 0;
	uint64_t start, end;
	uint32_t usedlen = 0;
	uint8_t *cp = 0;
	uint64_t val = 0;
	double *t = 0;
	double mu, sd;
	struct list_entry *attr = NULL, *vattr = NULL;
	const uint32_t le_sz = roundup8(18), vle_sz = roundup8(50);
	const char uidp[] = "        unidentified attributes page   ";

	if (test < 1 || test > 9)
		return;

	t = Calloc(numiter, sizeof(*t));
	attr = Calloc(numpg*numattr, le_sz);
	vattr = Calloc(numpg*numattr, vle_sz);
	if (!t || !attr)
		return;

	switch (test) {
	case 1:
	case 2:
	case 3:
		val = 4;
		ret = attr_set_attr(osd->dbc, 1, 1, 2, 22, &val, sizeof(val));
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, le_sz);
		ret = attr_get_attr(osd->dbc, 1, 1, 2, 22, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == 0);
		assert(usedlen == le_sz);
		cp = (uint8_t *)attr;
		test_le(2, 22, sizeof(val), &val, cp);
		ret = attr_delete_attr(osd->dbc, 1, 1, 2, 22);
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, le_sz);
		ret = attr_get_attr(osd->dbc, 1, 1, 2, 22, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == -ENOENT);
		break;
	case 4:
	case 5:
	case 6:
		if (numpg*numattr < 2) {
			fprintf(stderr, "numpg*numattr < 2\n");
			return;
		}
		val = 200;
		ret = attr_set_attr(osd->dbc, 1, 1, 2, 22, &val, sizeof(val));
		assert(ret == 0);
		val = 400;
		ret = attr_set_attr(osd->dbc, 1, 1, 4, 44, &val, sizeof(val));
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, 2*le_sz);
		ret = attr_get_all_attrs(osd->dbc, 1, 1, 2*le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == 0);
		assert(usedlen == 2*le_sz);
		val = 200;
		cp = (uint8_t *)attr;
		test_le(2, 22, sizeof(val), &val, cp);
		val = 400;
		cp += le_sz;
		test_le(4, 44, sizeof(val), &val, cp);
		ret = attr_delete_all(osd->dbc, 1, 1);
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, 2*le_sz);
		ret = attr_get_attr(osd->dbc, 1, 1, 2, 22, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == -ENOENT);
		ret = attr_get_attr(osd->dbc, 1, 1, 4, 44, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == -ENOENT);
		break;
	case 7:
		if (numpg*numattr < 4) {
			fprintf(stderr, "numpg*numattr < 4\n");
			goto out;
		}
		val = 200;
		ret = attr_set_attr(osd->dbc, 1, 1, 2, 22, &val, sizeof(val));
		assert(ret == 0);
		val = 400;
		ret = attr_set_attr(osd->dbc, 1, 1, 4, 44, &val, sizeof(val));
		assert(ret == 0);
		usedlen = 0;
		memset(vattr, 0, 2*vle_sz);
		ret = attr_get_dir_page(osd->dbc, 1, 1, USEROBJECT_PG,
					vle_sz*2, vattr, RTRVD_SET_ATTR_LIST,
					&usedlen);
		assert(ret == 0);
		assert(usedlen == vle_sz*2);
		cp = (uint8_t *)vattr;
		test_le(USEROBJECT_PG, 2, sizeof(uidp), uidp, cp);
		cp += vle_sz;
		test_le(USEROBJECT_PG, 4, sizeof(uidp), uidp, cp);
		ret = attr_delete_all(osd->dbc, 1, 1);
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, 2*le_sz);
		ret = attr_get_attr(osd->dbc, 1, 1, 2, 22, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == -ENOENT);
		ret = attr_get_attr(osd->dbc, 1, 1, 4, 44, le_sz, attr, 
				    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == -ENOENT);
		break;
	case 8:
		if (numpg*numattr < 4) {
			fprintf(stderr, "numpg*numattr < 4\n");
			goto out;
		}
		for (i = 0; i < 4; i++) {
			val = i*100 + 1;
			ret = attr_set_attr(osd->dbc, 1, 1, i, 1, &val, 
					    sizeof(val));
			assert(ret == 0);
			ret = attr_set_attr(osd->dbc, 1, 1, i, 2, &val, 
					    sizeof(val));
			assert(ret == 0);
		}
		usedlen = 0;
		memset(attr, 0, 4*le_sz);
		ret = attr_get_for_all_pages(osd->dbc, 1, 1, 1, 4*le_sz, attr,
					     RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == 0);
		assert(usedlen == 4*le_sz);
		cp = (uint8_t *)attr;
		for (i = 0; i < 4; i++) {
			val = i*100 + 1;
			test_le(i, 1, sizeof(val), &val, cp);
			cp += le_sz;
		}
		ret = attr_delete_all(osd->dbc, 1, 1);
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, 4*le_sz);
		for (i = 0; i < 4; i++) {
			ret = attr_get_attr(osd->dbc, 1, 1, i, 1, le_sz, attr, 
					    RTRVD_SET_ATTR_LIST, &usedlen);
			assert(ret == -ENOENT);
			ret = attr_get_attr(osd->dbc, 1, 1, i, 2, le_sz, attr, 
					    RTRVD_SET_ATTR_LIST, &usedlen);
			assert(ret == -ENOENT);
		}
		break;
	case 9:
		if (numpg*numattr < 2) {
			fprintf(stderr, "numpg*numattr < 2\n");
			goto out;
		}
		for (i = 0; i < 2; i++) {
			val = i*100 + 1;
			ret = attr_set_attr(osd->dbc, 1, 1, i, 1, &val, 
					    sizeof(val));
			assert(ret == 0);
			ret = attr_set_attr(osd->dbc, 1, 1, i, 2, &val, 
					    sizeof(val));
			assert(ret == 0);
		}
		usedlen = 0;
		memset(attr, 0, 2*le_sz);
		ret = attr_get_page_as_list(osd->dbc, 1, 1, 1, 2*le_sz, attr,
					    RTRVD_SET_ATTR_LIST, &usedlen);
		assert(ret == 0);
		assert(usedlen == 2*le_sz);
		cp = (uint8_t *)attr;
		val = 101;
		test_le(1, 1, 8, &val, cp);
		cp += le_sz;
		test_le(1, 2, 8, &val, cp);
		ret = attr_delete_all(osd->dbc, 1, 1);
		assert(ret == 0);
		usedlen = 0;
		memset(attr, 0, 2*le_sz);
		for (i = 0; i < 2; i++) {
			ret = attr_get_attr(osd->dbc, 1, 1, i, 1, le_sz, attr, 
					    RTRVD_SET_ATTR_LIST, &usedlen);
			assert(ret == -ENOENT);
			ret = attr_get_attr(osd->dbc, 1, 1, i, 2, le_sz, attr, 
					    RTRVD_SET_ATTR_LIST, &usedlen);
			assert(ret == -ENOENT);
		}
		break;
	default:
		goto out;
	}

	for (i = 0; i < numiter;  i++) {
		/* set up; time test no. 4 */
		t[i] = 0.0;

		switch (test) {
		case 2:
		case 3:
			/* set one attr used to get/del */
			val = 400;
			ret = attr_set_attr(osd->dbc, 2, 2, 1, 1, &val,
					    sizeof(val));
			assert(ret == 0);
		case 1:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
			pre_create_attrs(osd, numpg, numattr); 
			break;
		case 4: {
			int np, na;
			for (np = 1; np < numpg+1; np++) {
				for (na = 1; na < numattr+1; na++) {
					val = na;
					rdtsc(start);
					ret = attr_set_attr(osd->dbc, 1, 1,
							    np, na, &val, 
							    sizeof(val));
					rdtsc(end);
					assert(ret == 0);
					t[i] += (double)(end - start) / mhz;
				}
			}
			break;
		}
		default:
			goto out;
		}

		/* test */
		switch (test) {
		case 1:
			val = 400;
			rdtsc(start);
			ret = attr_set_attr(osd->dbc, 2, 2, 1, 1, 
					    &val, sizeof(val));
			rdtsc(end);
			assert(ret == 0);
			break;
		case 2:
			usedlen = 0;
			memset(attr, 0, le_sz);
			rdtsc(start);
			ret = attr_get_attr(osd->dbc, 2, 2, 1, 1, le_sz, attr, 
					    RTRVD_SET_ATTR_LIST, &usedlen);
			rdtsc(end);
			assert(ret == 0);
			assert(usedlen == le_sz);
			break;
		case 3:
			usedlen = 0;
			memset(attr, 0, le_sz);
			rdtsc(start);
			ret = attr_delete_attr(osd->dbc, 2, 2, 1, 1);
			rdtsc(end);
			assert(ret == 0);
			break;
		case 4:
			break;
		case 5:
			rdtsc(start);
			ret = attr_get_all_attrs(osd->dbc, 1, 1,
						 numpg*numattr*le_sz, attr,
						 RTRVD_SET_ATTR_LIST,
						 &usedlen);
			rdtsc(end);
			assert(ret == 0);
			assert(usedlen == (numpg*numattr*le_sz));
			break;
		case 6:
			rdtsc(start);
			ret = attr_delete_all(osd->dbc, 1, 1);
			rdtsc(end);
			assert(ret == 0);
			break;
		case 7:
			rdtsc(start);
			ret = attr_get_dir_page(osd->dbc, 1, 1, USEROBJECT_PG,
						numpg*vle_sz, vattr, 
						RTRVD_SET_ATTR_LIST, 
						&usedlen);
			rdtsc(end);
			assert(ret == 0);
			assert(usedlen == numpg*vle_sz);
			break;
		case 8:
			rdtsc(start);
			ret = attr_get_for_all_pages(osd->dbc, 1, 1, 1,
						     numpg*le_sz, attr,
						     RTRVD_SET_ATTR_LIST,
						     &usedlen);
			rdtsc(end);
			assert(ret == 0);
			assert(usedlen == numpg*le_sz);
			break;
		case 9:
			rdtsc(start);
			ret = attr_get_page_as_list(osd->dbc, 1, 1, 1,
						    numattr*le_sz, attr,
						    RTRVD_SET_ATTR_LIST,
						    &usedlen);
			rdtsc(end);
			assert(ret == 0);
			assert(usedlen == numattr*le_sz);
			break;
		default:
			goto out;
		}

		if (test != 4)
			t[i] = (double) (end - start) / mhz;
		end = start = 0;

		/* cleanup */
		switch (test) {
		case 1:
		case 2:
			ret = attr_delete_attr(osd->dbc, 2, 2, 1, 1);
			assert(ret == 0);
		case 3:
		case 4:
		case 5:
			break;
		case 6:
		case 7:
		case 8:
		case 9:
			ret = attr_delete_all(osd->dbc, 1, 1);
			assert(ret == 0);
			break;
		default:
			goto out;
		}
	}

	mu = mean(t, numiter);
	sd = stddev(t, mu, numiter);
	printf("%s numiter %d numpg %d numattr %d test %d avg %lf +- %lf "
	       " us\n", func, numiter, numpg, numattr, test, mu, sd); 

out:
	free(t);
	free(attr);
	free(vattr);
}
/* only to be used by test_set_one_attr */
static void set_one_attr_int(struct osd_device *osd, uint64_t pid, uint64_t oid, 
			 uint32_t page, uint32_t number, uint64_t val)
{
	struct osd_command cmd;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	uint64_t attrval;
	int ret;
	struct attribute_list attr = {
		.type = ATTR_SET,
		.page = page,
		.number = number,
		.len = 8,
		.val = &attrval,
	};

	set_htonll(&attrval, val);
	ret = osd_command_set_set_attributes(&cmd, pid, oid);
	assert(ret == 0);
	ret = osd_command_attr_build(&cmd, &attr, 1);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, cmd.outdata, cmd.outlen,
				&data_out, &data_out_len,
				sense_out, &senselen_out);
	assert(ret == 0);
	osd_command_attr_free(&cmd);
}

static void set_one_attr_val(struct osd_device *osd, uint64_t pid, uint64_t oid,
			 uint32_t page, uint32_t number, const void *val,
			 uint16_t len)
{
	struct osd_command cmd;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	int ret,i;
	struct attribute_list attr = {
		.type = ATTR_SET,
		.page = page,
		.number = number,
		.len = len,
		.val = (void *)(uintptr_t) val,
	};

	ret = osd_command_set_set_attributes(&cmd, pid, oid);
	assert(ret == 0);
	ret = osd_command_attr_build(&cmd, &attr, 1);

	
	ret = osdemu_cmd_submit(osd, cmd.cdb, cmd.outdata, cmd.outlen,
				&data_out, &data_out_len,
				sense_out, &senselen_out);
	assert(ret == 0);
	osd_command_attr_free(&cmd);

	/* output cdbfmt , length , value */
	printf("cdbfmt is: %x \n", cmd.cdb[11]);
	printf("length is: %x \n", cmd.cdb[61]);
	printf("value is: ");
 	for(i=0; i<=len; i++){
	        printf("%c", cmd.cdb[62+i]);
	}
	printf("\n");     
}

static void test_set_one_attr (struct osd_device *osd)
{
	struct osd_command cmd;
	uint64_t pid = USEROBJECT_PID_LB;
       	uint64_t oid = USEROBJECT_OID_LB;
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	int ret;
    	uint32_t page = USEROBJECT_PG + LUN_PG_LB;
       	
	/* create a partition*/
	ret = osd_command_set_create_partition(&cmd, pid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	
	/* creat one object */
	ret = osd_command_set_create(&cmd, USEROBJECT_PID_LB,
				     USEROBJECT_OID_LB, 1); 
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out, &data_out_len,
				sense_out, &senselen_out);
	assert(ret == 0);
		
	/* these cases should not generate error */
	set_one_attr_val(osd, pid, oid, page, 1, "test", 5);
	set_one_attr_val(osd, pid, oid, page, 1, "test_set_one_attr", 18);
	set_one_attr_int(osd, pid, oid, page, 1, 10);
	set_one_attr_int(osd, pid, oid, page, 1, 20);

	/* these cases must generate error */
	/* set_one_attr_val(osd, pid, oid, page, 1, "ttest_set_one_attr", 19); */
	/* set_one_attr_val(osd, pid, oid, page, 1, "", 0); */
}


/* only to be used by test_osd_query */
static void set_attr_int(struct osd_device *osd, uint64_t pid, uint64_t oid, 
			 uint32_t page, uint32_t number, uint64_t val)
{
	struct osd_command cmd;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	uint64_t attrval;
	int ret;
	struct attribute_list attr = {
		.type = ATTR_SET,
		.page = page,
		.number = number,
		.len = 8,
		.val = &attrval,
	};

	set_htonll(&attrval, val);
	ret = osd_command_set_set_attributes(&cmd, pid, oid);
	assert(ret == 0);
	ret = osd_command_attr_build(&cmd, &attr, 1);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, cmd.outdata, cmd.outlen,
				&data_out, &data_out_len,
				sense_out, &senselen_out);
	assert(ret == 0);
	osd_command_attr_free(&cmd);
}

static void set_attr_val(struct osd_device *osd, uint64_t pid, uint64_t oid,
			 uint32_t page, uint32_t number, const void *val,
			 uint16_t len)
{
	struct osd_command cmd;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	int ret;
	struct attribute_list attr = {
		.type = ATTR_SET,
		.page = page,
		.number = number,
		.len = len,
		.val = (void *)(uintptr_t) val,
	};

	ret = osd_command_set_set_attributes(&cmd, pid, oid);
	assert(ret == 0);
	ret = osd_command_attr_build(&cmd, &attr, 1);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, cmd.outdata, cmd.outlen,
				&data_out, &data_out_len,
				sense_out, &senselen_out);
	assert(ret == 0);
	osd_command_attr_free(&cmd);
}


static void set_qce(uint8_t *cp, uint32_t page, uint32_t number, 
		    uint16_t min_len, const void *min_val, 
		    uint16_t max_len, const void *max_val)
{
	uint16_t len = 4 + 4 + 2 + min_len + 2 + max_len;

	set_htons(&cp[2], len);
	set_htonl(&cp[4], page);
	set_htonl(&cp[8], number);
	set_htons(&cp[12], min_len);
	memcpy(&cp[14], min_val, min_len);
	set_htons(&cp[14+min_len], max_len);
	memcpy(&cp[16+min_len], max_val, max_len);
}

static int ismember(uint64_t needle, uint64_t *hay, uint64_t haysz)
{

	while (haysz--)
		if (needle == hay[haysz])
			return 1;
	return 0;
}

static void check_results(uint8_t *matches, uint64_t matchlen,
			  uint64_t *idlist, uint64_t idlistlen)
{
	uint32_t add_len = get_ntohll(&matches[0]);

	assert(add_len == (5+8*idlistlen));
	assert(matches[12] == (0x21 << 2));
	assert(matchlen == add_len+8);
	add_len -= 5;
	matches += MIN_ML_LEN;
	while (add_len) {
		assert(ismember(get_ntohll(matches), idlist, 8));
		matches += 8;
		add_len -= 8;
	}
}


void test_query(struct osd_device *osd)
{
	struct osd_command cmd;
	uint64_t pid = PARTITION_PID_LB;
	uint64_t cid = COLLECTION_OID_LB;
	uint64_t oid = USEROBJECT_OID_LB + 1;  /* leave room for cid */
	uint8_t *data_out = NULL;
	uint64_t data_out_len;
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	int i, ret;

	/* create a collection and stick some objects in it */
	ret = osd_command_set_create_partition(&cmd, pid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	ret = osd_command_set_create_collection(&cmd, pid, cid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* but don't put all of the objects in the collection */
	for (i=0; i<10; i++) {
		uint64_t attrval;
		struct attribute_list attr = {
			.type = ATTR_SET,
			.page = USER_COLL_PG,
			.number = 1,
			.len = 8,
			.val = &attrval,
		};
		set_htonll(&attrval, cid);
		ret = osd_command_set_create(&cmd, pid, oid + i, 1);
		assert(ret == 0);
		if (!(i == 2 || i == 8)) {
			ret = osd_command_attr_build(&cmd, &attr, 1);
			assert(ret == 0);
		}
		ret = osdemu_cmd_submit(osd, cmd.cdb, cmd.outdata, cmd.outlen,
					&data_out, &data_out_len,
					sense_out, &senselen_out);
		assert(ret == 0);
		osd_command_attr_free(&cmd);
	}

	/*
	 * Set some random attributes for querying.
	 */
	uint32_t page = USEROBJECT_PG + LUN_PG_LB;
	set_attr_int(osd, pid, oid,   page, 1, 4);
	set_attr_int(osd, pid, oid+1, page, 1, 49);
	set_attr_int(osd, pid, oid+1, page, 2, 130);
	set_attr_int(osd, pid, oid+2, page, 1, 20);
	set_attr_int(osd, pid, oid+3, page, 1, 101);
	set_attr_int(osd, pid, oid+4, page, 1, 59);
	set_attr_int(osd, pid, oid+4, page, 2, 37);
	set_attr_int(osd, pid, oid+5, page, 1, 75);
	set_attr_int(osd, pid, oid+6, page, 1, 200);
	set_attr_int(osd, pid, oid+7, page, 1, 67);
	set_attr_int(osd, pid, oid+8, page, 1, 323);
	set_attr_int(osd, pid, oid+8, page, 2, 44);
	set_attr_int(osd, pid, oid+9, page, 1, 1);
	set_attr_int(osd, pid, oid+9, page, 2, 19);

	/*
	 * Various queries.
	 */
	/* run without query criteria */
	uint8_t buf[1024], *cp, *matches;
	uint32_t qll;
	uint64_t matchlen;
	uint64_t idlist[8];

	qll = MINQLISTLEN;
	memset(buf, 0, 1024);
	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[0] = oid;
	idlist[1] = oid+1;
	idlist[2] = oid+3;
	idlist[3] = oid+4;
	idlist[4] = oid+5;
	idlist[5] = oid+6;
	idlist[6] = oid+7;
	idlist[7] = oid+9;
	check_results(matches, matchlen, idlist, 8);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run one query without min/max constraints */
	qll = 0;
	memset(buf, 0, 1024);
	cp = buf;
	set_qce(&cp[4], page, 2, 0, NULL, 0, NULL);
	qll += 4 + (4+4+4+2+2);

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[0] = oid+1; 
	idlist[1] = oid+4; 
	idlist[2] = oid+9;
	check_results(matches, matchlen, idlist, 3);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run one query with criteria */
	uint64_t min, max;
	qll = 0;
	min = 40, max= 80;
	set_htonll(&min, min);
	set_htonll(&max, max);
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x0;
	set_qce(&cp[4], page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[0] = oid+1; 
	idlist[1] = oid+4; 
	idlist[2] = oid+5;
	idlist[3] = oid+7;
	check_results(matches, matchlen, idlist, 4);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run union of two query criteria */
	qll = 0;

	/* first query */
	min = 100, max = 180;
	set_htonll(&min, min);
	set_htonll(&max, max);
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x0; /* UNION */
	set_qce(&cp[4], page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));

	/* second query */
	min = 200, max = 323;
	set_htonll(&min, min);
	set_htonll(&max, max);
	set_qce(cp, page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += (4+4+4+2+sizeof(min)+2+sizeof(max));

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[0] = oid+3; 
	idlist[1] = oid+6; 
	check_results(matches, matchlen, idlist, 2);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run intersection of 2 query criteria */
	qll = 0;

	/* first query */
	min = 4, max = 100;
	set_htonll(&min, min);
	set_htonll(&max, max);
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x1; /* INTERSECTION */
	set_qce(&cp[4], page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));

	/* second query */
	min = 10, max = 400;
	set_htonll(&min, min);
	set_htonll(&max, max);
	set_qce(cp, page, 2, sizeof(min), &min, sizeof(max), &max);
	qll += (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += (4+4+4+2+sizeof(min)+2+sizeof(max));

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[0] = oid+1; 
	idlist[1] = oid+4; 
	check_results(matches, matchlen, idlist, 2);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run union of 3 query criteria, with missing min/max */
	qll = 0;

	/* first query */
	min = 130, max = 130;
	set_htonll(&min, min);
	set_htonll(&max, max);
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x0; /* UNION */
	set_qce(&cp[4], page, 2, sizeof(min), &min, sizeof(max), &max);
	qll += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));

	/* second query */
	min = 150;
	set_htonll(&min, min);
	set_qce(cp, page, 1, sizeof(min), &min, 0, NULL);
	qll += (4+4+4+2+sizeof(min)+2+0);
	cp += (4+4+4+2+sizeof(min)+2+0);

	/* third query */
	max = 10;
	set_htonll(&max, max);
	set_qce(cp, page, 1, 0, NULL, sizeof(max), &max);
	qll += (4+4+4+2+0+2+sizeof(max));
	cp += (4+4+4+2+0+2+sizeof(max));

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);

	idlist[3] = oid;
	idlist[4] = oid+1;
	idlist[5] = oid+6;
	idlist[2] = oid+9;
	check_results(matches, matchlen, idlist, 4);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* set some attributes with text values */
	set_attr_val(osd, pid, oid,   page, 1, "hello", 6);
	set_attr_val(osd, pid, oid+1, page, 1, "cat", 4);
	set_attr_int(osd, pid, oid+1, page, 2, 130);
	set_attr_int(osd, pid, oid+2, page, 1, 20);
	set_attr_val(osd, pid, oid+3, page, 1, "zebra", 6);
	set_attr_int(osd, pid, oid+4, page, 1, 59);
	set_attr_int(osd, pid, oid+4, page, 2, 37);
	set_attr_int(osd, pid, oid+5, page, 1, 75);
	set_attr_val(osd, pid, oid+6, page, 1, "keema", 6);
	set_attr_int(osd, pid, oid+7, page, 1, 67);
	set_attr_int(osd, pid, oid+8, page, 1, 323);
	set_attr_int(osd, pid, oid+8, page, 2, 44);
	set_attr_int(osd, pid, oid+9, page, 1, 1);
	set_attr_val(osd, pid, oid+9, page, 2, "hotelling", 10);

	/* run queries on different datatypes, with diff min max lengths */
	qll = 0;

	/* first query */
	min = 41, max = 169;
	set_htonll(&min, min);
	set_htonll(&max, max);
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x0; /* UNION */
	set_qce(&cp[4], page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += 4 + (4+4+4+2+sizeof(min)+2+sizeof(max));

	/* second query */
	set_qce(cp, page, 1, 3, "ab", 5, "keta");
	qll += (4+4+4+2+2+2+5);
	cp += (4+4+4+2+2+2+5);

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);
	idlist[3] = oid;
	idlist[4] = oid+1;
	idlist[0] = oid+4; 
	idlist[1] = oid+5; 
	idlist[5] = oid+6;
	idlist[2] = oid+7;
	check_results(matches, matchlen, idlist, 6);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run intersection of 3 query criteria, with missing min/max */
	qll = 0;

	/* first query */
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x1; /* INTERSECTION */
	set_qce(&cp[4], page, 1, 2, "a", 3, "zz");
	qll += 4 + (4+4+4+2+2+2+3);
	cp += 4 + (4+4+4+2+2+2+3);

	/* second query */
	min = 140;
	set_htonll(&min, min);
	set_qce(cp, page, 1, sizeof(min), &min, 0, NULL);
	qll += (4+4+4+2+sizeof(min)+2+0);
	cp += (4+4+4+2+sizeof(min)+2+0);

	/* third query */
	set_qce(cp, page, 2, 0, NULL, 6, "alpha");
	qll += (4+4+4+2+0+2+6);
	cp += (4+4+4+2+0+2+6);

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);
	idlist[0] = oid+1;
	check_results(matches, matchlen, idlist, 1);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/* run intersection of 2 query criteria with empty result */
	qll = 0;

	/* first query */
	memset(buf, 0, 1024);
	cp = buf;
	cp[0] = 0x1; /* INTERSECTION */
	set_qce(&cp[4], page, 1, 3, "aa", 4, "zzz");
	qll += 4 + (4+4+4+2+3+2+4);
	cp += 4 + (4+4+4+2+3+2+4);

	/* second query */
	min = 50;
	max = 80;
	set_htonll(&min, min);
	set_htonll(&max, max);
	set_qce(cp, page, 1, sizeof(min), &min, sizeof(max), &max);
	qll += (4+4+4+2+sizeof(min)+2+sizeof(max));
	cp += (4+4+4+2+sizeof(min)+2+sizeof(max));

	ret = osd_command_set_query(&cmd, pid, cid, qll, 4096);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, buf, qll, &matches,
				&matchlen, sense_out, &senselen_out);
	assert(ret == 0);
	check_results(matches, matchlen, idlist, 0);

	free(matches);
	matches = NULL;
	matchlen = 0;

	/*
	 * Cleanup.
	 */
	for (i=0; i<10; i++) {
		ret = osd_command_set_remove(&cmd, pid, oid + i);
		assert(ret == 0);
		ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
					&data_out_len, sense_out,
					&senselen_out);
		assert(ret == 0);
	}
	ret = osd_command_set_remove_collection(&cmd, pid, cid, 0);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	ret = osd_command_set_remove_partition(&cmd, pid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
}

struct test_attr {
	uint8_t type;
	uint64_t oid;
	uint32_t page;
	uint32_t number;
	uint64_t intval;
	uint16_t valen;
	const void *val;
}; 

static void ismember_attr(struct test_attr *attr, size_t sz, uint64_t oid,
			  uint32_t page, uint32_t number, uint64_t valen,
			  const void *val)
{
	size_t i = 0;
	for (i = 0; i < sz; i++) {
		if (attr[i].oid == oid && attr[i].page == page &&
		    attr[i].number == number) {
			assert(valen <= attr[i].valen);
			if (attr[i].type == 1) {
				if (valen == attr[i].valen)
					assert(attr[i].intval == get_ntohll(val));
			} else {
				assert(memcmp(attr[i].val, val, valen) == 0);
			}
			return;
		}
	}
	fprintf(stderr, "unknown attr: oid: %llu, page %u, number %u\n",
		llu(oid), page, number);
	assert(0); /* unknown attr */
}

static void test_oids_with_attr(struct osd_device *osd, uint64_t pid, 
				struct attribute_list *getattr, int numattr,
				uint64_t alloc_len, uint64_t exp_data_out_len,
				uint64_t exp_add_len, uint64_t exp_cont_id,
				uint8_t exp_odf, struct test_attr *attrs,
				size_t attrs_sz)
{
	int ret = 0;
	struct osd_command cmd;
	uint8_t *cp = NULL;
	uint32_t page = 0, number = 0;
	uint64_t data_in_len, data_out_len;
	const void *data_in;
	uint8_t *data_out = NULL;
	uint64_t oid = 0; 
	uint8_t sense_out[OSD_MAX_SENSE];
	uint16_t len = 0;
	int senselen_out;
	uint32_t attr_list_len = 0;

	/* execute list with attr, alloc length less than required */
	ret = osd_command_set_list(&cmd, pid, 0, alloc_len, 0, 1);
	assert(ret == 0);
	ret = osd_command_attr_build(&cmd, getattr, numattr);
	assert(ret == 0);
	data_in = cmd.outdata;
	data_in_len = cmd.outlen;
	ret = osdemu_cmd_submit(osd, cmd.cdb, data_in, data_in_len, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	cp = data_out;
	assert(data_out_len == exp_data_out_len);
	assert(get_ntohll(cp) == exp_add_len);
	cp += 8;
	assert(get_ntohll(cp) == exp_cont_id);
	cp += 8;
	assert(get_ntohl(cp) == 0);
	cp += 7;
	assert(cp[0] == exp_odf);
	cp += 1;
	oid = 0;
	attr_list_len = 0;
	len = 0;
	data_out_len -= 24;
	while (data_out_len > 0) {
		oid = get_ntohll(cp);
		cp += 12;
		attr_list_len = get_ntohl(cp);
		cp += 4;
		data_out_len -= 16;
		while (attr_list_len > 0) {
			page = get_ntohl(cp);
			cp += 4;
			number = get_ntohl(cp);
			cp += 4;
			len = get_ntohs(cp);
			cp += 2;
			attr_list_len -= (4+4+2);
			data_out_len -= (4+4+2);
			if (len > attr_list_len) {
				len = attr_list_len;
			}
			ismember_attr(attrs, attrs_sz, oid, page, number,
				      len, cp);
			cp += len;
			cp += (roundup8(2+len) - (2+len));
			data_out_len -= len;
			data_out_len -= (roundup8(2+len) - (2+len));
			attr_list_len -= len;
			attr_list_len -= (roundup8(2+len) - (2+len)); 
		}
	}
	free(data_out);
	data_out = NULL;
	osd_command_attr_free(&cmd);
}

void test_list(struct osd_device *osd)
{
	struct osd_command cmd;
	uint64_t pid = PARTITION_PID_LB;
	uint64_t cid = 0;
	uint64_t oid = 0; 
	uint8_t *data_out = NULL;
	uint8_t *cp;
	uint32_t page = 0, number = 0;
	uint64_t data_out_len;
	uint64_t idlist[64];
	uint8_t sense_out[OSD_MAX_SENSE];
	int senselen_out;
	int i, ret;

	/* create partition */
	ret = osd_command_set_create_partition(&cmd, pid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* create collection */
	ret = osd_command_set_create_collection(&cmd, pid, cid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* create 6 objects */
	ret = osd_command_set_create(&cmd, pid, 0, 6);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out, 
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* create another collection */
	ret = osd_command_set_create_collection(&cmd, pid, cid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* create 4 objects */
	ret = osd_command_set_create(&cmd, pid, 0, 4);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out, 
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	/* set attributes on userobjects */
	page = USEROBJECT_PG + LUN_PG_LB;
	number = 1;
	oid = COLLECTION_OID_LB + 1;
	struct test_attr attrs[] = {
		{1, oid, page, number, 1, 8, NULL},
		{1, oid, page+1, number+1, 768, 8, NULL},
		{2, oid, page+2, number+2, 0, 5, "sudo"}, 
		{1, oid+1, page+1, number+1, 56, 8, NULL},
		{1, oid+1, page+2, number+2, 68, 8, NULL},
		{2, oid+2, page+2, number+2, 0, 9, "deadbeef"}, 
		{1, oid+3, page+3, number+3, 1, 8, NULL},
		{1, oid+3, page+1, number+1, 111, 8, NULL},
		{2, oid+3, page+4, number+4, 0, 5, "sudo"}, 
		{1, oid+3, page+2, number+2, 11, 8, NULL},
		{1, oid+3, page+5, number+5, 111111, 8, NULL},
		{2, oid+4, page+4, number+4, 0, 6, "milli"}, 
		{2, oid+4, page+5, number+5, 0, 10, "kilometer"},
		{2, oid+4, page+3, number+3, 0, 11, "hectameter"},
		{2, oid+5, page+1, number+1, 0, 12, "zzzzzzhhhhh"}, 
		{2, oid+5, page+2, number+2, 0, 2, "b"},
		{1, oid+5, page+3, number+3, 6, 8, NULL},
		{1, oid+7, page+1, number+1, 486, 8, NULL}, 
		{1, oid+7, page+4, number+4, 586, 8, NULL},
		{1, oid+7, page+2, number+2, 686, 8, NULL},
		{1, oid+8, page, number, 4, 8, NULL},
		{2, oid+9, page+1, number+1, 0, 14, "setting these"}, 
		{2, oid+9, page+2, number+2, 0, 11, "attributes"},
		{2, oid+9, page+3, number+3, 0, 8, "made me"},
		{2, oid+9, page+4, number+4, 0, 12, "mad! really"}, 
		{1, oid+10, page+1, number+1, 1234567890, 8, NULL},
		{2, oid+10, page, number, 0, 6, "DelTa"},
	};
	for (i = 0; i < ARRAY_SIZE(attrs); i++) {
		if (attrs[i].type == 1) {
			set_attr_int(osd, pid, attrs[i].oid, attrs[i].page,
				     attrs[i].number, attrs[i].intval);
		} else {
			set_attr_val(osd, pid, attrs[i].oid, attrs[i].page,
				     attrs[i].number, attrs[i].val,
				     attrs[i].valen);
		}
	}

	/* set some attributes on collections */
	page = COLLECTION_PG + LUN_PG_LB;
	cid = COLLECTION_OID_LB;
	set_attr_int(osd, pid, cid,   page, 1, 1);
	set_attr_int(osd, pid, cid+7, page, 2, 2);

	/* execute list command. get only oids first */
	ret = osd_command_set_list(&cmd, pid, 0, 4096, 0, 0);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	cp = data_out;
	assert(get_ntohll(cp) == 10*8+16);
	assert(data_out_len == 10*8+24);
	cp += 8;
	assert(get_ntohll(cp) == 0);
	cp += 8;
	assert(get_ntohl(cp) == 0);
	cp += 7;
	assert(cp[0] == (0x21 << 2));
	cp += 1;
	data_out_len -= 24;
	oid = COLLECTION_OID_LB + 1;
	for (i = 0; i < 6; i++)
		idlist[i] = oid + i;
	oid = COLLECTION_OID_LB + 1 + i + 1;
	for (i = 0; i < 4; i++)
		idlist[6+i] = oid + i;
	while (data_out_len > 0) {
		assert(ismember(get_ntohll(cp), idlist, 10));
		cp += 8;
		data_out_len -= 8;
	}
	free(data_out);
	data_out = NULL;
	osd_command_attr_free(&cmd);

	/* execute list command with less space */
	ret = osd_command_set_list(&cmd, pid, 0, 72, 0, 0);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	cp = data_out;
	assert(get_ntohll(cp) == 10*8+16);
	assert(data_out_len == 72);
	cp += 8;
	assert(get_ntohll(cp) == COLLECTION_OID_LB + 8);
	cp += 8;
	assert(get_ntohl(cp) == 0);
	cp += 7;
	assert(cp[0] == (0x21 << 2));
	cp += 1;
	data_out_len -= 24;
	oid = COLLECTION_OID_LB + 1;
	for (i = 0; i < 6; i++)
		idlist[i] = oid + i;
	for (i = 0; i < 4; i++)
		idlist[6+i] = 0;
	while (data_out_len > 0) {
		assert(ismember(get_ntohll(cp), idlist, 10));
		cp += 8;
		data_out_len -= 8;
	}
	free(data_out);
	data_out = NULL;
	osd_command_attr_free(&cmd);

	page = USEROBJECT_PG + LUN_PG_LB;
	number = 1;
	struct attribute_list getattr[] = {
		{ATTR_GET, page, number, NULL, 0, 0},
		{ATTR_GET, page+1, number+1, NULL, 0, 0},
		{ATTR_GET, page+2, number+2, NULL, 0, 0},
		{ATTR_GET, page+3, number+3, NULL, 0, 0},
		{ATTR_GET, page+4, number+4, NULL, 0, 0},
		{ATTR_GET, page+5, number+5, NULL, 0, 0},
	};

	/* execute list with attr */
	test_oids_with_attr(osd, pid, getattr, 6, 4096, 792, 784, 0, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 200, 200, 784, 65539, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 208, 208, 784, 65539, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 216, 208, 784, 65539, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 544, 536, 784, 65544, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 688, 688, 784, 65546, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));
	/* execute list with attr, alloc length less than required */
	test_oids_with_attr(osd, pid, getattr, 6, 680, 680, 784, 65546, 
			    (0x22 << 2), attrs, ARRAY_SIZE(attrs));

	/* clean up */
	oid = USEROBJECT_OID_LB;
	for (i=0; i<12; i++) {
		if (i == 0 || i == 7)
			continue;
		ret = osd_command_set_remove(&cmd, pid, oid + i);
		assert(ret == 0);
		ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
					&data_out_len, sense_out,
					&senselen_out);
		assert(ret == 0);
	}
	cid = COLLECTION_OID_LB;
	ret = osd_command_set_remove_collection(&cmd, pid, cid, 0);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
	cid = COLLECTION_OID_LB + 7;
	ret = osd_command_set_remove_collection(&cmd, pid, cid, 0);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);

	ret = osd_command_set_remove_partition(&cmd, pid);
	assert(ret == 0);
	ret = osdemu_cmd_submit(osd, cmd.cdb, NULL, 0, &data_out,
				&data_out_len, sense_out, &senselen_out);
	assert(ret == 0);
}

static void test_attr_vals(uint8_t *cp, struct attribute_list *attrs, 
			   size_t sz)
{
	size_t i = 0;
	uint32_t page = 0;
	uint32_t num = 0;
	uint16_t len = 0;
	uint32_t list_len = 0;

	assert((cp[0] & 0x0F) == 0x9);
	cp += 4;
	list_len = get_ntohl(cp);
	cp += 4;
	while (list_len > 0) {
		page = get_ntohl(cp);
		cp += 4;
		num = get_ntohl(cp);
		cp += 4;
		len = get_ntohs(cp);
		cp += 2;
		for (i = 0; i < sz; i++) {
			if (!(attrs[i].page==page && attrs[i].number==num)) 
				continue;

			assert(len == attrs[i].len);
			if (len == 8) {
				assert(get_ntohll(attrs[i].val) ==
				       get_ntohll(cp));
			} else if (len != 0) {
				assert(memcmp(attrs[i].val, cp, len) == 0);
			}
			break;
		}
		assert(i < sz);
		if (len == 0) {
			cp += (roundup8(10) - 10);
			list_len -= roundup8(4+4+2);
		} else {
			cp += len;
			cp += (roundup8(2+len) - (2+len));
			list_len -= roundup8(4+4+2+len);
		}
	}
	assert(list_len == 0);
}