From a776d98e5f349dbb7f5a47eca48f50e6117adcb7 Mon Sep 17 00:00:00 2001 From: Robert Mustacchi Date: Sat, 19 Jul 2014 11:00:57 -0700 Subject: [PATCH] 5030 VNICs should support a variable MTU Reviewed by: Jerry Jelinek Reviewed by: Rob Gulewich Reviewed by: Igor Kozhukhov Reviewed by: Dan McDonald Approved by: Richard Lowe --- usr/src/lib/libdladm/common/libdladm.c | 3 + usr/src/lib/libdladm/common/libdladm.h | 3 +- usr/src/lib/libdladm/common/libdlvnic.c | 2 + usr/src/uts/common/io/mac/mac.c | 105 ++++++++++++++++++++++++++++++++ usr/src/uts/common/io/mac/mac_client.c | 10 ++- usr/src/uts/common/io/vnic/vnic_dev.c | 100 +++++++++++++++++++++++++----- usr/src/uts/common/sys/mac.h | 5 +- usr/src/uts/common/sys/mac_impl.h | 9 +++ usr/src/uts/common/sys/vnic.h | 3 +- usr/src/uts/common/sys/vnic_impl.h | 3 +- 10 files changed, 223 insertions(+), 20 deletions(-) diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c index 5d44a912e7..cf113e7357 100644 --- a/usr/src/lib/libdladm/common/libdladm.c +++ b/usr/src/lib/libdladm/common/libdladm.c @@ -415,6 +415,9 @@ dladm_status2str(dladm_status_t status, char *buf) case DLADM_STATUS_PORT_NOPROTO: s = "local or remote port requires transport"; break; + case DLADM_STATUS_INVALID_MTU: + s = "MTU check failed, MTU outside of device's supported range"; + break; default: s = ""; break; diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h index f0811ae5df..c2fceb25ab 100644 --- a/usr/src/lib/libdladm/common/libdladm.h +++ b/usr/src/lib/libdladm/common/libdladm.h @@ -172,7 +172,8 @@ typedef enum { DLADM_STATUS_INVALID_PKEY, DLADM_STATUS_NO_IB_HW_RESOURCE, DLADM_STATUS_INVALID_PKEY_TBL_SIZE, - DLADM_STATUS_PORT_NOPROTO + DLADM_STATUS_PORT_NOPROTO, + DLADM_STATUS_INVALID_MTU } dladm_status_t; typedef enum { diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c index 6dba8d6fad..44f8bb2726 100644 --- a/usr/src/lib/libdladm/common/libdlvnic.c +++ b/usr/src/lib/libdladm/common/libdlvnic.c @@ -90,6 +90,8 @@ dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag) return (DLADM_STATUS_NO_HWRINGS); case VNIC_IOC_DIAG_MACADDR_INVALID: return (DLADM_STATUS_INVALIDMACADDR); + case VNIC_IOC_DIAG_MACMTU_INVALID: + return (DLADM_STATUS_INVALID_MTU); default: return (DLADM_STATUS_FAILED); } diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c index 7939228339..ed809e5f45 100644 --- a/usr/src/uts/common/io/mac/mac.c +++ b/usr/src/uts/common/io/mac/mac.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -2685,6 +2686,110 @@ mac_margin_update(mac_handle_t mh, uint32_t margin) } /* + * MAC clients use this interface to request that a MAC device not change its + * MTU below the specified amount. At this time, that amount must be within the + * range of the device's current minimum and the device's current maximum. eg. a + * client cannot request a 3000 byte MTU when the device's MTU is currently + * 2000. + * + * If "current" is set to B_TRUE, then the request is to simply to reserve the + * current underlying mac's maximum for this mac client and return it in mtup. + */ +int +mac_mtu_add(mac_handle_t mh, uint32_t *mtup, boolean_t current) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + mac_mtu_req_t *prev, *cur; + mac_propval_range_t mpr; + int err; + + i_mac_perim_enter(mip); + rw_enter(&mip->mi_rw_lock, RW_WRITER); + + if (current == B_TRUE) + *mtup = mip->mi_sdu_max; + mpr.mpr_count = 1; + err = mac_prop_info(mh, MAC_PROP_MTU, "mtu", NULL, 0, &mpr, NULL); + if (err != 0) { + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (err); + } + + if (*mtup > mip->mi_sdu_max || + *mtup < mpr.mpr_range_uint32[0].mpur_min) { + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (ENOTSUP); + } + + prev = NULL; + for (cur = mip->mi_mtrp; cur != NULL; cur = cur->mtr_nextp) { + if (*mtup == cur->mtr_mtu) { + cur->mtr_ref++; + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (0); + } + + if (*mtup > cur->mtr_mtu) + break; + + prev = cur; + } + + cur = kmem_alloc(sizeof (mac_mtu_req_t), KM_SLEEP); + cur->mtr_mtu = *mtup; + cur->mtr_ref = 1; + if (prev != NULL) { + cur->mtr_nextp = prev->mtr_nextp; + prev->mtr_nextp = cur; + } else { + cur->mtr_nextp = mip->mi_mtrp; + mip->mi_mtrp = cur; + } + + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (0); +} + +int +mac_mtu_remove(mac_handle_t mh, uint32_t mtu) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + mac_mtu_req_t *cur, *prev; + + i_mac_perim_enter(mip); + rw_enter(&mip->mi_rw_lock, RW_WRITER); + + prev = NULL; + for (cur = mip->mi_mtrp; cur != NULL; cur = cur->mtr_nextp) { + if (cur->mtr_mtu == mtu) { + ASSERT(cur->mtr_ref > 0); + cur->mtr_ref--; + if (cur->mtr_ref == 0) { + if (prev == NULL) { + mip->mi_mtrp = cur->mtr_nextp; + } else { + prev->mtr_nextp = cur->mtr_nextp; + } + kmem_free(cur, sizeof (mac_mtu_req_t)); + } + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (0); + } + + prev = cur; + } + + rw_exit(&mip->mi_rw_lock); + i_mac_perim_exit(mip); + return (ENOENT); +} + +/* * MAC Type Plugin functions. */ diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c index ae2a39ff49..078d0e816d 100644 --- a/usr/src/uts/common/io/mac/mac_client.c +++ b/usr/src/uts/common/io/mac/mac_client.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -5242,6 +5242,14 @@ mac_set_mtu(mac_handle_t mh, uint_t new_mtu, uint_t *old_mtu_arg) goto bail; } + rw_enter(&mip->mi_rw_lock, RW_READER); + if (mip->mi_mtrp != NULL && new_mtu < mip->mi_mtrp->mtr_mtu) { + rv = EBUSY; + rw_exit(&mip->mi_rw_lock); + goto bail; + } + rw_exit(&mip->mi_rw_lock); + if (old_mtu != new_mtu) { rv = mip->mi_callbacks->mc_setprop(mip->mi_driver, "mtu", MAC_PROP_MTU, sizeof (uint_t), &new_mtu); diff --git a/usr/src/uts/common/io/vnic/vnic_dev.c b/usr/src/uts/common/io/vnic/vnic_dev.c index 2af812547e..4615f00e51 100644 --- a/usr/src/uts/common/io/vnic/vnic_dev.c +++ b/usr/src/uts/common/io/vnic/vnic_dev.c @@ -499,10 +499,21 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu, &mac->m_max_sdu); + err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE); + if (err != 0) { + VERIFY(mac_margin_remove(vnic->vn_lower_mh, + vnic->vn_margin) == 0); + mac_free(mac); + if (diag != NULL) + *diag = VNIC_IOC_DIAG_MACMTU_INVALID; + goto bail; + } + vnic->vn_mtu = mac->m_max_sdu; } else { vnic->vn_margin = VLAN_TAGSZ; mac->m_min_sdu = 1; mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU; + vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU; } mac->m_margin = vnic->vn_margin; @@ -510,8 +521,12 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, err = mac_register(mac, &vnic->vn_mh); mac_free(mac); if (err != 0) { - VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, - vnic->vn_margin) == 0); + if (!is_anchor) { + VERIFY(mac_mtu_remove(vnic->vn_lower_mh, + vnic->vn_mtu) == 0); + VERIFY(mac_margin_remove(vnic->vn_lower_mh, + vnic->vn_margin) == 0); + } goto bail; } @@ -526,6 +541,10 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, } err = mac_client_set_resources(vnic->vn_mch, mrp); if (err != 0) { + VERIFY(mac_mtu_remove(vnic->vn_lower_mh, + vnic->vn_mtu) == 0); + VERIFY(mac_margin_remove(vnic->vn_lower_mh, + vnic->vn_margin) == 0); (void) mac_unregister(vnic->vn_mh); goto bail; } @@ -536,6 +555,12 @@ vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid, if (err != 0) { VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin) == 0); + if (!is_anchor) { + VERIFY(mac_mtu_remove(vnic->vn_lower_mh, + vnic->vn_mtu) == 0); + VERIFY(mac_margin_remove(vnic->vn_lower_mh, + vnic->vn_margin) == 0); + } (void) mac_unregister(vnic->vn_mh); goto bail; } @@ -654,6 +679,7 @@ vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp) vnic->vn_slot_id); } (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin); + (void) mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu); (void) mac_notify_remove(vnic->vn_mnh, B_TRUE); (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh); mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC); @@ -1002,19 +1028,34 @@ vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num, case MAC_PROP_MTU: { uint32_t mtu; - /* allow setting MTU only on an etherstub */ - if (vn->vn_link_id != DATALINK_INVALID_LINKID) - return (err); - if (pr_valsize < sizeof (mtu)) { err = EINVAL; break; } bcopy(pr_val, &mtu, sizeof (mtu)); - if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) { - err = EINVAL; - break; + + if (vn->vn_link_id == DATALINK_INVALID_LINKID) { + if (mtu < ANCHOR_VNIC_MIN_MTU || + mtu > ANCHOR_VNIC_MAX_MTU) { + err = EINVAL; + break; + } + } else { + err = mac_mtu_add(vn->vn_lower_mh, &mtu, B_FALSE); + /* + * If it's not supported to set a value here, translate + * that to EINVAL, so user land gets a better idea of + * what went wrong. This realistically means that they + * violated the output of prop info. + */ + if (err == ENOTSUP) + err = EINVAL; + if (err != 0) + break; + VERIFY(mac_mtu_remove(vn->vn_lower_mh, + vn->vn_mtu) == 0); } + vn->vn_mtu = mtu; err = mac_maxsdu_update(vn->vn_mh, mtu); break; } @@ -1058,14 +1099,43 @@ static void vnic_m_propinfo(void *m_driver, const char *pr_name, { vnic_t *vn = m_driver; - /* MTU setting allowed only on an etherstub */ - if (vn->vn_link_id != DATALINK_INVALID_LINKID) - return; - switch (pr_num) { case MAC_PROP_MTU: - mac_prop_info_set_range_uint32(prh, - ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU); + if (vn->vn_link_id == DATALINK_INVALID_LINKID) { + mac_prop_info_set_range_uint32(prh, + ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU); + } else { + uint32_t max; + mac_perim_handle_t mph; + mac_propval_range_t range; + + /* + * The valid range for a VNIC's MTU is the minimum that + * the device supports and the current value of the + * device. A VNIC cannot increase the current MTU of the + * device. Therefore we need to get the range from the + * propinfo endpoint and current mtu from the + * traditional property endpoint. + */ + mac_perim_enter_by_mh(vn->vn_lower_mh, &mph); + if (mac_get_prop(vn->vn_lower_mh, MAC_PROP_MTU, "mtu", + &max, sizeof (uint32_t)) != 0) { + mac_perim_exit(mph); + return; + } + + range.mpr_count = 1; + if (mac_prop_info(vn->vn_lower_mh, MAC_PROP_MTU, "mtu", + NULL, 0, &range, NULL) != 0) { + mac_perim_exit(mph); + return; + } + + mac_prop_info_set_default_uint32(prh, max); + mac_prop_info_set_range_uint32(prh, + range.mpr_range_uint32[0].mpur_min, max); + mac_perim_exit(mph); + } break; } } diff --git a/usr/src/uts/common/sys/mac.h b/usr/src/uts/common/sys/mac.h index f1bfa1aec2..bd09077f1b 100644 --- a/usr/src/uts/common/sys/mac.h +++ b/usr/src/uts/common/sys/mac.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #ifndef _SYS_MAC_H @@ -617,6 +617,9 @@ extern void mac_margin_get(mac_handle_t, uint32_t *); extern int mac_margin_remove(mac_handle_t, uint32_t); extern int mac_margin_add(mac_handle_t, uint32_t *, boolean_t); +extern int mac_mtu_add(mac_handle_t, uint32_t *, + boolean_t); +extern int mac_mtu_remove(mac_handle_t, uint32_t); extern int mac_fastpath_disable(mac_handle_t); extern void mac_fastpath_enable(mac_handle_t); extern void mac_no_active(mac_handle_t); diff --git a/usr/src/uts/common/sys/mac_impl.h b/usr/src/uts/common/sys/mac_impl.h index 8f9f23ff71..3c9c6b77a4 100644 --- a/usr/src/uts/common/sys/mac_impl.h +++ b/usr/src/uts/common/sys/mac_impl.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #ifndef _SYS_MAC_IMPL_H @@ -68,6 +69,13 @@ struct mac_margin_req_s { uint32_t mmr_margin; }; +typedef struct mac_mtu_req_s mac_mtu_req_t; +struct mac_mtu_req_s { + mac_mtu_req_t *mtr_nextp; + uint_t mtr_ref; + uint32_t mtr_mtu; +}; + /* Generic linked chain type */ typedef struct mac_chain_s { struct mac_chain_s *next; @@ -533,6 +541,7 @@ struct mac_impl_s { * sorted: the first one has the greatest value. */ mac_margin_req_t *mi_mmrp; + mac_mtu_req_t *mi_mtrp; char **mi_priv_prop; uint_t mi_priv_prop_count; diff --git a/usr/src/uts/common/sys/vnic.h b/usr/src/uts/common/sys/vnic.h index 3a6f5279ee..e18673a6c1 100644 --- a/usr/src/uts/common/sys/vnic.h +++ b/usr/src/uts/common/sys/vnic.h @@ -55,7 +55,8 @@ typedef enum { VNIC_IOC_DIAG_MACPREFIX_INVALID, VNIC_IOC_DIAG_MACPREFIXLEN_INVALID, VNIC_IOC_DIAG_MACMARGIN_INVALID, - VNIC_IOC_DIAG_NO_HWRINGS + VNIC_IOC_DIAG_NO_HWRINGS, + VNIC_IOC_DIAG_MACMTU_INVALID } vnic_ioc_diag_t; /* diff --git a/usr/src/uts/common/sys/vnic_impl.h b/usr/src/uts/common/sys/vnic_impl.h index ffaa2939f5..7e50091347 100644 --- a/usr/src/uts/common/sys/vnic_impl.h +++ b/usr/src/uts/common/sys/vnic_impl.h @@ -21,7 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2013 Joyent, Inc. All rights reserved. + * Copyright 2014 Joyent, Inc. All rights reserved. */ #ifndef _SYS_VNIC_IMPL_H @@ -64,6 +64,7 @@ typedef struct vnic_s { mac_notify_handle_t vn_mnh; uint32_t vn_hcksum_txflags; + uint32_t vn_mtu; } vnic_t; #define vn_mch vn_mc_handles[0] -- 2.11.4.GIT