/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _SYS_MULTIDATA_IMPL_H
#define	_SYS_MULTIDATA_IMPL_H

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#ifdef	__cplusplus
extern "C" {
#endif

/*
 * Multidata: implementation-private data structure and declarations.
 */

/*
 * Structure used for insque/remque circular list operations.
 */
typedef struct ql_s {
	struct ql_s *ql_next;	/* pointer to next list element */
	struct ql_s *ql_prev;	/* pointer to previous list element */
} ql_t;

#define	QL_INIT(q) {				\
	((ql_t *)(q))->ql_next = (ql_t *)(q);	\
	((ql_t *)(q))->ql_prev = (ql_t *)(q);	\
}

typedef struct pdesc_slab_s pdesc_slab_t;

/*
 * Attribute hash bucket structure.
 */
typedef struct patbkt_s {
	kmutex_t pbkt_lock;	/* per-bucket lock */
	ql_t	pbkt_pattr_q;	/* list of attributes */
	uint_t	pbkt_tbl_sz;	/* table size (if this is first bucket) */
} patbkt_t;

/*
 * Attribute structure.
 */
#define	PATTR_MAGIC	0x50615472	/* "PaTr" */

struct pattr_s {
	pattr_t *pat_next;	/* pointer to next attribute in bucket */
	pattr_t *pat_prev;	/* pointer to previous attribute in bucket */

	uint_t pat_magic;	/* set to PATTR_MAGIC */

	kmutex_t *pat_lock;	/* pointer to per-bucket lock */
	multidata_t *pat_mmd;	/* back pointer to Multidata */
	uint_t	pat_buflen;	/* length of this structure + attribute */
	uint_t	pat_type;	/* type of encapsulated attribute */
	uint_t	pat_flags;	/* misc. flags */
};

/*
 * Values for pat_flags.
 */
#define	PATTR_REM_DEFER	0x1	/* entry is marked unusable but still exists */
#define	PATTR_PERSIST	0x2	/* entry can't be removed */

#define	Q2PATTR(p)	\
	((pattr_t *)((caddr_t)(p) - offsetof(pattr_t, pat_next)))

/*
 * Packet descriptor structure.
 */
#define	PDESC_MAGIC	0x506b5464	/* "PkTd" */

struct pdesc_s {
	pdesc_t	*pd_next;	/* pointer to next descriptor */
	pdesc_t	*pd_prev;	/* pointer to previous descriptor */

	uint_t pd_magic;	/* set to PDESC_MAGIC */

	pdesc_slab_t *pd_slab;	/* back pointer to descriptor slab */
	patbkt_t *pd_pattbl;	/* hash table of local attributes */

	pdescinfo_t pd_pdi;	/* embedded descriptor info structure */

#define	pd_flags	pd_pdi.flags
};

/*
 * Additional internal flags for pd_flags (see multidata.h for the rest).
 */
#define	PDESC_REM_DEFER	0x1000	/* entry is marked unusable but still exists */
#define	PDESC_HAS_REF	(PDESC_HBUF_REF | PDESC_PBUF_REF)

#define	Q2PD(p)		\
	((pdesc_t *)((caddr_t)(p) - offsetof(pdesc_t, pd_next)))

#define	PDI_COPY(pd_src, pd_dst) {				\
	(pd_dst)->flags = (pd_src)->flags & PDESC_HAS_REF;	\
	if ((pd_dst)->flags & PDESC_HBUF_REF) {			\
		(pd_dst)->hdr_base = (pd_src)->hdr_base;	\
		(pd_dst)->hdr_rptr = (pd_src)->hdr_rptr;	\
		(pd_dst)->hdr_wptr = (pd_src)->hdr_wptr;	\
		(pd_dst)->hdr_lim = (pd_src)->hdr_lim;		\
	} else {						\
		(pd_dst)->hdr_base = NULL;			\
		(pd_dst)->hdr_rptr = NULL;			\
		(pd_dst)->hdr_wptr = NULL;			\
		(pd_dst)->hdr_lim = NULL;			\
	}							\
								\
	if ((pd_dst)->flags & PDESC_PBUF_REF) {			\
		int i;						\
								\
		(pd_dst)->pld_cnt = (pd_src)->pld_cnt;		\
		for (i = 0; i < (pd_dst)->pld_cnt; i++) {	\
			(pd_dst)->pld_ary[i].pld_pbuf_idx =	\
			    (pd_src)->pld_ary[i].pld_pbuf_idx;	\
			(pd_dst)->pld_ary[i].pld_rptr =		\
			    (pd_src)->pld_ary[i].pld_rptr;	\
			(pd_dst)->pld_ary[i].pld_wptr =		\
			    (pd_src)->pld_ary[i].pld_wptr;	\
		}						\
	} else {						\
		(pd_dst)->pld_cnt = 0;				\
	}							\
}

/*
 * Packet descriptor slab structure.
 */
struct pdesc_slab_s {
	pdesc_slab_t *pds_next;	/* pointer to next descriptor slab */
	pdesc_slab_t *pds_prev;	/* pointer to previous descriptor slab */

	multidata_t *pds_mmd;	/* back pointer to Multidata */
	uint_t	pds_used;	/* always-increasing index to array */
	uint_t	pds_sz;		/* size of descriptor array */

	pdesc_t	pds_free_desc[1]; /* array of available descriptors */
};

#define	Q2PDSLAB(p)	\
	((pdesc_slab_t *)((caddr_t)(p) - offsetof(pdesc_slab_t, pds_next)))

#define	PDESC_SLAB_SIZE(npd)  \
	((size_t)(&((pdesc_slab_t *)0)->pds_free_desc[npd]))

/*
 * Multidata metadata structure.
 */
#define	MULTIDATA_MAGIC	0x4d645461	/* "MdTa" */

struct multidata_s {
	uint_t	mmd_magic;	/* set to MULTIDATA_MAGIC */

	dblk_t	*mmd_dp;	/* back pointer to wrapper dblk structure */
	mblk_t	*mmd_hbuf;	/* pointer to header buffer mblk */

	patbkt_t *mmd_pattbl;	/* hash table of global attributes */

	kmutex_t mmd_pd_slab_lock; /* lock to protect the following items */
	uint_t	mmd_pbuf_cnt;	/* number of data buffer */
	mblk_t	*mmd_pbuf[MULTIDATA_MAX_PBUFS];	/* data buffer mblk(s) */
	ql_t	mmd_pd_slab_q;	/* list of packet descriptor slabs */
	ql_t	mmd_pd_q;	/* list of packet descriptors */
	uint_t	mmd_slab_cnt;	/* number of packet descriptor slabs */
	uint_t	mmd_pd_cnt;	/* number of in-use packet desciptors */
	uint_t	mmd_hbuf_ref;	/* descriptors referring to header buffer */
	uint_t	mmd_pbuf_ref;	/* descriptors referring to payload buffer(s) */
};

#ifdef _KERNEL

extern void mmd_init(void);
extern mblk_t *mmd_copy(mblk_t *, int);

#endif /* _KERNEL */

#ifdef	__cplusplus
}
#endif

#endif	/* _SYS_MULTIDATA_IMPL_H */