cxgb3i_ddp.h 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * cxgb3i_ddp.h: Chelsio S3xx iSCSI DDP Manager.
 *
 * Copyright (c) 2008 Chelsio Communications, Inc.
 *
 * 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.
 *
 * Written by: Karen Xie (kxie@chelsio.com)
 */

#ifndef __CXGB3I_ULP2_DDP_H__
#define __CXGB3I_ULP2_DDP_H__

16 17
#include <linux/vmalloc.h>

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
/**
 * struct cxgb3i_tag_format - cxgb3i ulp tag format for an iscsi entity
 *
 * @sw_bits:	# of bits used by iscsi software layer
 * @rsvd_bits:	# of bits used by h/w
 * @rsvd_shift:	h/w bits shift left
 * @rsvd_mask:	reserved bit mask
 */
struct cxgb3i_tag_format {
	unsigned char sw_bits;
	unsigned char rsvd_bits;
	unsigned char rsvd_shift;
	unsigned char filler[1];
	u32 rsvd_mask;
};

/**
 * struct cxgb3i_gather_list - cxgb3i direct data placement memory
 *
 * @tag:	ddp tag
 * @length:	total data buffer length
 * @offset:	initial offset to the 1st page
 * @nelem:	# of pages
 * @pages:	page pointers
 * @phys_addr:	physical address
 */
struct cxgb3i_gather_list {
	u32 tag;
	unsigned int length;
	unsigned int offset;
	unsigned int nelem;
	struct page **pages;
	dma_addr_t phys_addr[0];
};

/**
 * struct cxgb3i_ddp_info - cxgb3i direct data placement for pdu payload
 *
 * @list:	list head to link elements
 * @tdev:	pointer to t3cdev used by cxgb3 driver
 * @max_txsz:	max tx packet size for ddp
 * @max_rxsz:	max rx packet size for ddp
 * @llimit:	lower bound of the page pod memory
 * @ulimit:	upper bound of the page pod memory
 * @nppods:	# of page pod entries
 * @idx_last:	page pod entry last used
 * @idx_bits:	# of bits the pagepod index would take
 * @idx_mask:	pagepod index mask
 * @rsvd_tag_mask: tag mask
 * @map_lock:	lock to synchonize access to the page pod map
 * @gl_map:	ddp memory gather list
 * @gl_skb:	skb used to program the pagepod
 */
struct cxgb3i_ddp_info {
	struct list_head list;
	struct t3cdev *tdev;
	struct pci_dev *pdev;
	unsigned int max_txsz;
	unsigned int max_rxsz;
	unsigned int llimit;
	unsigned int ulimit;
	unsigned int nppods;
	unsigned int idx_last;
	unsigned char idx_bits;
	unsigned char filler[3];
	u32 idx_mask;
	u32 rsvd_tag_mask;
	spinlock_t map_lock;
	struct cxgb3i_gather_list **gl_map;
	struct sk_buff **gl_skb;
};

90
#define ISCSI_PDU_NONPAYLOAD_LEN	312 /* bhs(48) + ahs(256) + digest(8) */
91
#define ULP2_MAX_PKT_SIZE	16224
92
#define ULP2_MAX_PDU_PAYLOAD	(ULP2_MAX_PKT_SIZE - ISCSI_PDU_NONPAYLOAD_LEN)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
#define PPOD_PAGES_MAX		4
#define PPOD_PAGES_SHIFT	2	/* 4 pages per pod */

/*
 * struct pagepod_hdr, pagepod - pagepod format
 */
struct pagepod_hdr {
	u32 vld_tid;
	u32 pgsz_tag_clr;
	u32 maxoffset;
	u32 pgoffset;
	u64 rsvd;
};

struct pagepod {
	struct pagepod_hdr hdr;
	u64 addr[PPOD_PAGES_MAX + 1];
};

#define PPOD_SIZE		sizeof(struct pagepod)	/* 64 */
#define PPOD_SIZE_SHIFT		6

#define PPOD_COLOR_SHIFT	0
#define PPOD_COLOR_SIZE		6
#define PPOD_COLOR_MASK		((1 << PPOD_COLOR_SIZE) - 1)

#define PPOD_IDX_SHIFT		PPOD_COLOR_SIZE
#define PPOD_IDX_MAX_SIZE	24

#define S_PPOD_TID    0
#define M_PPOD_TID    0xFFFFFF
#define V_PPOD_TID(x) ((x) << S_PPOD_TID)

#define S_PPOD_VALID    24
#define V_PPOD_VALID(x) ((x) << S_PPOD_VALID)
#define F_PPOD_VALID    V_PPOD_VALID(1U)

#define S_PPOD_COLOR    0
#define M_PPOD_COLOR    0x3F
#define V_PPOD_COLOR(x) ((x) << S_PPOD_COLOR)

#define S_PPOD_TAG    6
#define M_PPOD_TAG    0xFFFFFF
#define V_PPOD_TAG(x) ((x) << S_PPOD_TAG)

#define S_PPOD_PGSZ    30
#define M_PPOD_PGSZ    0x3
#define V_PPOD_PGSZ(x) ((x) << S_PPOD_PGSZ)

/*
 * large memory chunk allocation/release
 * use vmalloc() if kmalloc() fails
 */
static inline void *cxgb3i_alloc_big_mem(unsigned int size,
					 gfp_t gfp)
{
	void *p = kmalloc(size, gfp);
	if (!p)
		p = vmalloc(size);
	if (p)
		memset(p, 0, size);
	return p;
}

static inline void cxgb3i_free_big_mem(void *addr)
{
	if (is_vmalloc_addr(addr))
		vfree(addr);
	else
		kfree(addr);
}

/*
 * cxgb3i ddp tag are 32 bits, it consists of reserved bits used by h/w and
 * non-reserved bits that can be used by the iscsi s/w.
 * The reserved bits are identified by the rsvd_bits and rsvd_shift fields
 * in struct cxgb3i_tag_format.
 *
 * The upper most reserved bit can be used to check if a tag is ddp tag or not:
 * 	if the bit is 0, the tag is a valid ddp tag
 */

/**
 * cxgb3i_is_ddp_tag - check if a given tag is a hw/ddp tag
 * @tformat: tag format information
 * @tag: tag to be checked
 *
 * return true if the tag is a ddp tag, false otherwise.
 */
static inline int cxgb3i_is_ddp_tag(struct cxgb3i_tag_format *tformat, u32 tag)
{
	return !(tag & (1 << (tformat->rsvd_bits + tformat->rsvd_shift - 1)));
}

/**
 * cxgb3i_sw_tag_usable - check if a given s/w tag has enough bits left for
 *			  the reserved/hw bits
 * @tformat: tag format information
 * @sw_tag: s/w tag to be checked
 *
 * return true if the tag is a ddp tag, false otherwise.
 */
static inline int cxgb3i_sw_tag_usable(struct cxgb3i_tag_format *tformat,
					u32 sw_tag)
{
	sw_tag >>= (32 - tformat->rsvd_bits);
	return !sw_tag;
}

/**
 * cxgb3i_set_non_ddp_tag - mark a given s/w tag as an invalid ddp tag
 * @tformat: tag format information
 * @sw_tag: s/w tag to be checked
 *
 * insert 1 at the upper most reserved bit to mark it as an invalid ddp tag.
 */
static inline u32 cxgb3i_set_non_ddp_tag(struct cxgb3i_tag_format *tformat,
					 u32 sw_tag)
{
	unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
	u32 mask = (1 << shift) - 1;

	if (sw_tag && (sw_tag & ~mask)) {
		u32 v1 = sw_tag & ((1 << shift) - 1);
		u32 v2 = (sw_tag >> (shift - 1)) << shift;

		return v2 | v1 | 1 << shift;
	}
	return sw_tag | 1 << shift;
}

/**
 * cxgb3i_ddp_tag_base - shift the s/w tag bits so that reserved bits are not
 *			 used.
 * @tformat: tag format information
 * @sw_tag: s/w tag to be checked
 */
static inline u32 cxgb3i_ddp_tag_base(struct cxgb3i_tag_format *tformat,
				      u32 sw_tag)
{
	u32 mask = (1 << tformat->rsvd_shift) - 1;

	if (sw_tag && (sw_tag & ~mask)) {
		u32 v1 = sw_tag & mask;
		u32 v2 = sw_tag >> tformat->rsvd_shift;

		v2 <<= tformat->rsvd_shift + tformat->rsvd_bits;
		return v2 | v1;
	}
	return sw_tag;
}

/**
 * cxgb3i_tag_rsvd_bits - get the reserved bits used by the h/w
 * @tformat: tag format information
 * @tag: tag to be checked
 *
 * return the reserved bits in the tag
 */
static inline u32 cxgb3i_tag_rsvd_bits(struct cxgb3i_tag_format *tformat,
				       u32 tag)
{
	if (cxgb3i_is_ddp_tag(tformat, tag))
		return (tag >> tformat->rsvd_shift) & tformat->rsvd_mask;
	return 0;
}

/**
 * cxgb3i_tag_nonrsvd_bits - get the non-reserved bits used by the s/w
 * @tformat: tag format information
 * @tag: tag to be checked
 *
 * return the non-reserved bits in the tag.
 */
static inline u32 cxgb3i_tag_nonrsvd_bits(struct cxgb3i_tag_format *tformat,
					  u32 tag)
{
	unsigned char shift = tformat->rsvd_bits + tformat->rsvd_shift - 1;
	u32 v1, v2;

	if (cxgb3i_is_ddp_tag(tformat, tag)) {
		v1 = tag & ((1 << tformat->rsvd_shift) - 1);
		v2 = (tag >> (shift + 1)) << tformat->rsvd_shift;
	} else {
		u32 mask = (1 << shift) - 1;

		tag &= ~(1 << shift);
		v1 = tag & mask;
		v2 = (tag >> 1) & ~mask;
	}
	return v1 | v2;
}

int cxgb3i_ddp_tag_reserve(struct t3cdev *, unsigned int tid,
			   struct cxgb3i_tag_format *, u32 *tag,
			   struct cxgb3i_gather_list *, gfp_t gfp);
void cxgb3i_ddp_tag_release(struct t3cdev *, u32 tag);

struct cxgb3i_gather_list *cxgb3i_ddp_make_gl(unsigned int xferlen,
				struct scatterlist *sgl,
				unsigned int sgcnt,
				struct pci_dev *pdev,
				gfp_t gfp);
void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl,
				struct pci_dev *pdev);

int cxgb3i_setup_conn_host_pagesize(struct t3cdev *, unsigned int tid,
				    int reply);
int cxgb3i_setup_conn_pagesize(struct t3cdev *, unsigned int tid, int reply,
			       unsigned long pgsz);
int cxgb3i_setup_conn_digest(struct t3cdev *, unsigned int tid,
				int hcrc, int dcrc, int reply);
int cxgb3i_ddp_find_page_index(unsigned long pgsz);
int cxgb3i_adapter_ddp_init(struct t3cdev *, struct cxgb3i_tag_format *,
			    unsigned int *txsz, unsigned int *rxsz);
void cxgb3i_adapter_ddp_cleanup(struct t3cdev *);
#endif