root/usr/src/uts/common/io/ib/clients/rds/rds_opt.c
/*
 * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 1990 Mentat Inc.
 */

#include <sys/ib/clients/rds/rds.h>
#include <inet/proto_set.h>

#define rds_max_buf 2097152
opdes_t rds_opt_arr[] = {

{ SO_TYPE,      SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
{ SO_SNDBUF,    SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
{ SO_RCVBUF,    SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
};

/* ARGSUSED */
int
rds_opt_default(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
{
        /* no default value processed by protocol specific code currently */
        return (-1);
}

/*
 * This routine retrieves the current status of socket options.
 * It returns the size of the option retrieved.
 */
int
rds_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
{
        int     *i1 = (int *)(uintptr_t)ptr;

        switch (level) {
        case SOL_SOCKET:
                switch (name) {
                case SO_TYPE:
                        *i1 = SOCK_DGRAM;
                        break;  /* goto sizeof (int) option return */

                case SO_SNDBUF:
                        *i1 = q->q_hiwat;
                        break;  /* goto sizeof (int) option return */
                case SO_RCVBUF:
                        *i1 = RD(q)->q_hiwat;
                        break;  /* goto sizeof (int) option return */
                default:
                        return (-1);
                }
                break;
        default:
                return (-1);
        }
        return (sizeof (int));
}

/* This routine sets socket options. */
/* ARGSUSED */
int
rds_opt_set(queue_t *q, uint_t optset_context, int level,
    int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
    uchar_t *outvalp, void *thisdg_attrs, cred_t *cr)
{
        int     *i1 = (int *)(uintptr_t)invalp;
        boolean_t checkonly;

        switch (optset_context) {
        case SETFN_OPTCOM_CHECKONLY:
                checkonly = B_TRUE;
                /*
                 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
                 * inlen != 0 implies value supplied and
                 *      we have to "pretend" to set it.
                 * inlen == 0 implies that there is no
                 *      value part in T_CHECK request and just validation
                 * done elsewhere should be enough, we just return here.
                 */
                if (inlen == 0) {
                        *outlenp = 0;
                        return (0);
                }
                break;
        case SETFN_OPTCOM_NEGOTIATE:
                checkonly = B_FALSE;
                break;
        default:
                /*
                 * We should never get here
                 */
                *outlenp = 0;
                return (EINVAL);
        }

        ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
            (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));

        /*
         * For fixed length options, no sanity check
         * of passed in length is done. It is assumed *_optcom_req()
         * routines do the right thing.
         */

        switch (level) {
        case SOL_SOCKET:
                switch (name) {

                case SO_SNDBUF:
                        if (*i1 > rds_max_buf) {
                                *outlenp = 0;
                                return (ENOBUFS);
                        }
                        if (!checkonly) {
                                q->q_hiwat = *i1;
                        }
                        break;
                case SO_RCVBUF:
                        if (*i1 > rds_max_buf) {
                                *outlenp = 0;
                                return (ENOBUFS);
                        }
                        if (!checkonly) {
                                RD(q)->q_hiwat = *i1;
                                (void) proto_set_rx_hiwat(RD(q), NULL, *i1);
                        }
                        break;
                default:
                        *outlenp = 0;
                        return (EINVAL);
                }
                break;
        default:
                *outlenp = 0;
                return (EINVAL);
        }
        /*
         * Common case of OK return with outval same as inval.
         */
        if (invalp != outvalp) {
                /* don't trust bcopy for identical src/dst */
                (void) bcopy(invalp, outvalp, inlen);
        }
        *outlenp = inlen;
        return (0);
}

uint_t rds_max_optsize; /* initialized when RDS driver is loaded */

#define RDS_VALID_LEVELS_CNT    A_CNT(rds_valid_levels_arr)

#define RDS_OPT_ARR_CNT         A_CNT(rds_opt_arr)


optlevel_t rds_valid_levels_arr[] = {
        SOL_SOCKET,
};

/*
 * Initialize option database object for RDS
 *
 * This object represents database of options to search passed to
 * {sock,tpi}optcom_req() interface routine to take care of option
 * management and associated methods.
 */

optdb_obj_t rds_opt_obj = {
        rds_opt_default,        /* RDS default value function pointer */
        rds_opt_get,            /* RDS get function pointer */
        rds_opt_set,            /* RDS set function pointer */
        RDS_OPT_ARR_CNT,        /* RDS option database count of entries */
        rds_opt_arr,            /* RDS option database */
        RDS_VALID_LEVELS_CNT,   /* RDS valid level count of entries */
        rds_valid_levels_arr    /* RDS valid level array */
};