1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/atomic.h> 28 #include <sys/systm.h> 29 #include <sys/socket.h> 30 #include <netinet/in.h> 31 #include <sys/modctl.h> 32 #include <sys/sunddi.h> 33 #include <ipp/ipp.h> 34 #include <ipp/ipp_config.h> 35 #include <inet/common.h> 36 #include <ipp/dlcosmk/dlcosmk_impl.h> 37 38 #define D_SM_COMMENT "IPP dlcosmk marker module" 39 40 /* DDI file for dlcosmk ipp module */ 41 42 static int dlcosmk_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t); 43 static int dlcosmk_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t); 44 static int dlcosmk_destroy_action(ipp_action_id_t, ipp_flags_t); 45 static int dlcosmk_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *, 46 ipp_flags_t); 47 static int dlcosmk_invoke_action(ipp_action_id_t, ipp_packet_t *); 48 49 static int dlcosmk_statinit(ipp_action_id_t, dlcosmk_data_t *); 50 static int dlcosmk_update_stats(ipp_stat_t *, void *, int); 51 52 /* Entry points for this IPP module */ 53 ipp_ops_t dlcosmk_ops = { 54 IPPO_REV, 55 dlcosmk_create_action, /* ippo_action_create */ 56 dlcosmk_modify_action, /* ippo_action_modify */ 57 dlcosmk_destroy_action, /* ippo_action_destroy */ 58 dlcosmk_info, /* ippo_action_info */ 59 dlcosmk_invoke_action /* ippo_action_invoke */ 60 }; 61 62 extern struct mod_ops mod_ippops; 63 64 /* 65 * Module linkage information for the kernel. 66 */ 67 static struct modlipp modlipp = { 68 &mod_ippops, 69 D_SM_COMMENT, 70 &dlcosmk_ops 71 }; 72 73 static struct modlinkage modlinkage = { 74 MODREV_1, 75 { (void *)&modlipp, NULL } 76 }; 77 78 79 int 80 _init(void) 81 { 82 return (mod_install(&modlinkage)); 83 } 84 85 int 86 _fini(void) 87 { 88 return (mod_remove(&modlinkage)); 89 } 90 91 int 92 _info(struct modinfo *modinfop) 93 { 94 return (mod_info(&modlinkage, modinfop)); 95 } 96 97 static int 98 dlcosmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, 99 ipp_flags_t flags) 100 { 101 nvlist_t *nvlp; 102 dlcosmk_data_t *dlcosmk_data; 103 char *next_action; 104 int err; 105 uint32_t bstats, param; 106 107 ASSERT((nvlpp != NULL) && (*nvlpp != NULL)); 108 109 nvlp = *nvlpp; 110 *nvlpp = NULL; /* nvlist should be NULL on return */ 111 112 if ((dlcosmk_data = kmem_zalloc(DLCOSMK_DATA_SZ, KM_NOSLEEP)) == NULL) { 113 nvlist_free(nvlp); 114 return (ENOMEM); 115 } 116 117 /* parse next action name */ 118 if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 119 &next_action)) != 0) { 120 nvlist_free(nvlp); 121 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 122 "next_action name missing\n")); 123 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 124 return (err); 125 } 126 if ((dlcosmk_data->next_action = 127 ipp_action_lookup(next_action)) == IPP_ACTION_INVAL) { 128 nvlist_free(nvlp); 129 dlcosmk0dbg(("dlcosmk_create_action: next_action invalid\n")); 130 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 131 return (EINVAL); 132 } 133 134 /* parse cos - from the config file */ 135 if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, 136 &dlcosmk_data->usr_pri)) != 0) { 137 nvlist_free(nvlp); 138 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 139 "cos missing\n")); 140 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 141 return (err); 142 } 143 144 /* parse b_band - mapped from cos */ 145 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, ¶m)) != 0) { 146 nvlist_free(nvlp); 147 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 148 "b_band missing\n")); 149 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 150 return (err); 151 } 152 dlcosmk_data->b_band = param; 153 154 /* parse dl_priority.dl_max - mapped from cos */ 155 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, ¶m)) != 0) { 156 nvlist_free(nvlp); 157 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 158 "dl_priority missing\n")); 159 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 160 return (err); 161 } 162 dlcosmk_data->dl_max = param; 163 164 /* parse gather_stats boolean */ 165 if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats)) 166 != 0) { 167 dlcosmk_data->gather_stats = B_FALSE; 168 } else { 169 /* If stats is needed, initialize the stats structure */ 170 dlcosmk_data->gather_stats = (bstats != 0) ? B_TRUE : B_FALSE; 171 if (dlcosmk_data->gather_stats) { 172 if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) { 173 nvlist_free(nvlp); 174 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 175 return (err); 176 } 177 } 178 } 179 180 /* Free the nvlist */ 181 nvlist_free(nvlp); 182 183 /* set action chain reference */ 184 if ((err = ipp_action_ref(aid, dlcosmk_data->next_action, 185 flags)) != 0) { 186 dlcosmk0dbg(("dlcosmk_create_action: ipp_action_ref " \ 187 "returned with error %d\n", err)); 188 ipp_stat_destroy(dlcosmk_data->stats); 189 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 190 return (err); 191 } 192 193 ipp_action_set_ptr(aid, (void *)dlcosmk_data); 194 return (0); 195 } 196 197 static int 198 dlcosmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags) 199 { 200 nvlist_t *nvlp; 201 int err = 0; 202 uint32_t band, dlpri; 203 uint8_t config_type; 204 uint8_t cos; 205 char *next_action_name; 206 ipp_action_id_t next_action; 207 dlcosmk_data_t *dlcosmk_data; 208 uint32_t bstats; 209 210 ASSERT((nvlpp != NULL) && (*nvlpp != NULL)); 211 212 nvlp = *nvlpp; 213 *nvlpp = NULL; /* nvlist should be NULL when this returns */ 214 215 if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type)) 216 != 0) { 217 nvlist_free(nvlp); 218 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\ 219 "type\n")); 220 return (err); 221 } 222 223 if (config_type != IPP_SET) { 224 nvlist_free(nvlp); 225 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\ 226 "type %d\n", config_type)); 227 return (EINVAL); 228 } 229 230 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 231 ASSERT(dlcosmk_data != NULL); 232 233 /* parse next action name, if present */ 234 if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 235 &next_action_name)) == 0) { 236 /* lookup action name to get action id */ 237 if ((next_action = ipp_action_lookup(next_action_name)) 238 == IPP_ACTION_INVAL) { 239 nvlist_free(nvlp); 240 dlcosmk0dbg(("dlcosmk_modify_action: next_action "\ 241 "invalid\n")); 242 return (EINVAL); 243 } 244 /* reference new action */ 245 if ((err = ipp_action_ref(aid, next_action, flags)) != 0) { 246 nvlist_free(nvlp); 247 dlcosmk0dbg(("dlcosmk_modify_action: ipp_action_ref "\ 248 "returned with error %d\n", err)); 249 return (err); 250 } 251 /* unref old action */ 252 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags); 253 ASSERT(err == 0); 254 dlcosmk_data->next_action = next_action; 255 } 256 257 /* parse cos, if present */ 258 if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, &cos)) == 0) { 259 260 /* parse b_band, mapped from cos */ 261 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, 262 &band)) != 0) { 263 nvlist_free(nvlp); 264 dlcosmk0dbg(("dlcosmk_modify_action: b_band not "\ 265 "provided\n")); 266 return (err); 267 } 268 269 /* parse dl_priority, mapped from cos */ 270 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, 271 &dlpri)) != 0) { 272 nvlist_free(nvlp); 273 dlcosmk0dbg(("dlcosmk_modify_action: dl_priority not "\ 274 "provided\n")); 275 return (err); 276 } 277 278 /* Have all the three values, change them */ 279 dlcosmk_data->usr_pri = cos; 280 dlcosmk_data->b_band = band; 281 dlcosmk_data->dl_max = dlpri; 282 } 283 284 285 /* parse gather_stats boolean, if present */ 286 if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats)) 287 == 0) { 288 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE; 289 /* Turning on stats */ 290 if (!dlcosmk_data->gather_stats && val) { 291 if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) { 292 nvlist_free(nvlp); 293 return (err); 294 } 295 /* Turning off stats */ 296 } else if (!val && dlcosmk_data->gather_stats) { 297 ipp_stat_destroy(dlcosmk_data->stats); 298 299 } 300 dlcosmk_data->gather_stats = val; 301 } 302 303 /* Free thenvlist */ 304 nvlist_free(nvlp); 305 return (0); 306 } 307 308 static int 309 dlcosmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags) 310 { 311 dlcosmk_data_t *dlcosmk_data; 312 int err; 313 314 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 315 ASSERT(dlcosmk_data != NULL); 316 317 /* Destroy stats, if gathered */ 318 if (dlcosmk_data->gather_stats) { 319 ipp_stat_destroy(dlcosmk_data->stats); 320 } 321 322 /* unreference the action */ 323 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags); 324 ASSERT(err == 0); 325 326 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 327 return (0); 328 } 329 330 static int 331 dlcosmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet) 332 { 333 dlcosmk_data_t *dlcosmk_data; 334 mblk_t *mp = NULL; 335 int err; 336 ip_priv_t *priv; 337 338 ASSERT(packet != NULL); 339 340 /* get mblk from ipp_packet structure */ 341 mp = ipp_packet_get_data(packet); 342 priv = (ip_priv_t *)ipp_packet_get_private(packet); 343 344 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 345 ASSERT(dlcosmk_data != NULL); 346 347 /* dlcosmk packet as configured */ 348 if ((err = dlcosmk_process(&mp, dlcosmk_data, priv->ill_index, 349 priv->proc)) != 0) { 350 return (err); 351 } else { 352 /* return packet with next action set */ 353 return (ipp_packet_next(packet, dlcosmk_data->next_action)); 354 } 355 } 356 357 static int 358 dlcosmk_statinit(ipp_action_id_t aid, dlcosmk_data_t *dlcosmk_data) 359 { 360 int err; 361 dlcosmk_stat_t *statp; 362 363 /* install stats entry */ 364 if ((err = ipp_stat_create(aid, DLCOSMK_STATS_STRING, 365 DLCOSMK_STATS_COUNT, dlcosmk_update_stats, dlcosmk_data, 366 &dlcosmk_data->stats)) != 0) { 367 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_create " \ 368 "returned with error %d\n", err)); 369 return (err); 370 } 371 372 statp = (dlcosmk_stat_t *)(dlcosmk_data->stats)->ipps_data; 373 ASSERT(statp != NULL); 374 375 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "npackets", 376 IPP_STAT_UINT64, &statp->npackets)) != 0) { 377 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 378 "returned with error %d\n", err)); 379 return (err); 380 } 381 382 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "ipackets", 383 IPP_STAT_UINT64, &statp->ipackets)) != 0) { 384 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 385 "returned with error %d\n", err)); 386 return (err); 387 } 388 389 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "epackets", 390 IPP_STAT_UINT64, &statp->epackets)) != 0) { 391 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 392 "returned with error %d\n", err)); 393 return (err); 394 } 395 396 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "usr_pri", 397 IPP_STAT_INT32, &statp->usr_pri)) != 0) { 398 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 399 "returned with error %d", err)); 400 return (err); 401 } 402 403 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "b_band", 404 IPP_STAT_INT32, &statp->b_band)) != 0) { 405 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 406 "returned with error %d\n", err)); 407 return (err); 408 } 409 410 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "dl_max", 411 IPP_STAT_INT32, &statp->dl_max)) != 0) { 412 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 413 "returned with error %d\n", err)); 414 return (err); 415 } 416 417 ipp_stat_install(dlcosmk_data->stats); 418 return (0); 419 } 420 421 static int 422 dlcosmk_update_stats(ipp_stat_t *sp, void *arg, int rw) 423 { 424 dlcosmk_data_t *dlcosmk_data = (dlcosmk_data_t *)arg; 425 dlcosmk_stat_t *snames = (dlcosmk_stat_t *)sp->ipps_data; 426 uint32_t upri, bband; 427 428 ASSERT(dlcosmk_data != NULL); 429 ASSERT(snames != NULL); 430 431 upri = dlcosmk_data->usr_pri; 432 bband = dlcosmk_data->b_band; 433 434 (void) ipp_stat_named_op(&snames->npackets, &dlcosmk_data->npackets, 435 rw); 436 (void) ipp_stat_named_op(&snames->ipackets, &dlcosmk_data->ipackets, 437 rw); 438 (void) ipp_stat_named_op(&snames->epackets, &dlcosmk_data->epackets, 439 rw); 440 (void) ipp_stat_named_op(&snames->usr_pri, &upri, rw); 441 (void) ipp_stat_named_op(&snames->b_band, &bband, rw); 442 (void) ipp_stat_named_op(&snames->dl_max, &dlcosmk_data->dl_max, rw); 443 444 return (0); 445 } 446 447 /* ARGSUSED */ 448 static int 449 dlcosmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg, 450 ipp_flags_t flags) 451 { 452 nvlist_t *nvlp; 453 dlcosmk_data_t *dlcosmk_data; 454 char *next_action; 455 int err; 456 457 ASSERT(fn != NULL); 458 459 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 460 ASSERT(dlcosmk_data != NULL); 461 462 /* allocate nvlist to be passed back */ 463 if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) { 464 dlcosmk0dbg(("dlcosmk_info: error allocating memory\n")); 465 return (err); 466 } 467 468 /* look up next action with the next action id */ 469 if ((err = ipp_action_name(dlcosmk_data->next_action, 470 &next_action)) != 0) { 471 dlcosmk0dbg(("dlcosmk_info: next action not available\n")); 472 nvlist_free(nvlp); 473 return (err); 474 } 475 476 /* add next action name */ 477 if ((err = nvlist_add_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 478 next_action)) != 0) { 479 dlcosmk0dbg(("dlcosmk_info: error adding next action\n")); 480 nvlist_free(nvlp); 481 kmem_free(next_action, (strlen(next_action) + 1)); 482 return (err); 483 } 484 485 /* free action name */ 486 kmem_free(next_action, (strlen(next_action) + 1)); 487 488 /* add config type */ 489 if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) { 490 dlcosmk0dbg(("dlcosmk_info: error adding config. type\n")); 491 nvlist_free(nvlp); 492 return (err); 493 } 494 495 /* just give the cos, since that is what is provided in the config */ 496 if ((err = nvlist_add_byte(nvlp, DLCOSMK_COS, dlcosmk_data->usr_pri)) 497 != 0) { 498 dlcosmk0dbg(("dlcosmk_info: error adding cos\n")); 499 nvlist_free(nvlp); 500 return (err); 501 } 502 503 /* add gather stats boolean */ 504 if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE, 505 (dlcosmk_data->gather_stats ? 1 : 0))) != 0) { 506 dlcosmk0dbg(("dlcosmk_info: error adding stats status\n")); 507 nvlist_free(nvlp); 508 return (err); 509 } 510 511 /* call back with nvlist */ 512 err = fn(nvlp, arg); 513 514 nvlist_free(nvlp); 515 return (err); 516 }