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, 76 NULL 77 }; 78 79 80 int 81 _init(void) 82 { 83 return (mod_install(&modlinkage)); 84 } 85 86 int 87 _fini(void) 88 { 89 return (mod_remove(&modlinkage)); 90 } 91 92 int 93 _info(struct modinfo *modinfop) 94 { 95 return (mod_info(&modlinkage, modinfop)); 96 } 97 98 static int 99 dlcosmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, 100 ipp_flags_t flags) 101 { 102 nvlist_t *nvlp; 103 dlcosmk_data_t *dlcosmk_data; 104 char *next_action; 105 int err; 106 uint32_t bstats, param; 107 108 ASSERT((nvlpp != NULL) && (*nvlpp != NULL)); 109 110 nvlp = *nvlpp; 111 *nvlpp = NULL; /* nvlist should be NULL on return */ 112 113 if ((dlcosmk_data = kmem_zalloc(DLCOSMK_DATA_SZ, KM_NOSLEEP)) == NULL) { 114 nvlist_free(nvlp); 115 return (ENOMEM); 116 } 117 118 /* parse next action name */ 119 if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 120 &next_action)) != 0) { 121 nvlist_free(nvlp); 122 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 123 "next_action name missing\n")); 124 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 125 return (err); 126 } 127 if ((dlcosmk_data->next_action = 128 ipp_action_lookup(next_action)) == IPP_ACTION_INVAL) { 129 nvlist_free(nvlp); 130 dlcosmk0dbg(("dlcosmk_create_action: next_action invalid\n")); 131 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 132 return (EINVAL); 133 } 134 135 /* parse cos - from the config file */ 136 if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, 137 &dlcosmk_data->usr_pri)) != 0) { 138 nvlist_free(nvlp); 139 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 140 "cos missing\n")); 141 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 142 return (err); 143 } 144 145 /* parse b_band - mapped from cos */ 146 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, ¶m)) != 0) { 147 nvlist_free(nvlp); 148 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 149 "b_band missing\n")); 150 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 151 return (err); 152 } 153 dlcosmk_data->b_band = param; 154 155 /* parse dl_priority.dl_max - mapped from cos */ 156 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, ¶m)) != 0) { 157 nvlist_free(nvlp); 158 dlcosmk0dbg(("dlcosmk_create_action: invalid config, "\ 159 "dl_priority missing\n")); 160 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 161 return (err); 162 } 163 dlcosmk_data->dl_max = param; 164 165 /* parse gather_stats boolean */ 166 if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats)) 167 != 0) { 168 dlcosmk_data->gather_stats = B_FALSE; 169 } else { 170 /* If stats is needed, initialize the stats structure */ 171 dlcosmk_data->gather_stats = (bstats != 0) ? B_TRUE : B_FALSE; 172 if (dlcosmk_data->gather_stats) { 173 if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) { 174 nvlist_free(nvlp); 175 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 176 return (err); 177 } 178 } 179 } 180 181 /* Free the nvlist */ 182 nvlist_free(nvlp); 183 184 /* set action chain reference */ 185 if ((err = ipp_action_ref(aid, dlcosmk_data->next_action, 186 flags)) != 0) { 187 dlcosmk0dbg(("dlcosmk_create_action: ipp_action_ref " \ 188 "returned with error %d\n", err)); 189 ipp_stat_destroy(dlcosmk_data->stats); 190 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 191 return (err); 192 } 193 194 ipp_action_set_ptr(aid, (void *)dlcosmk_data); 195 return (0); 196 } 197 198 static int 199 dlcosmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags) 200 { 201 nvlist_t *nvlp; 202 int err = 0; 203 uint32_t band, dlpri; 204 uint8_t config_type; 205 uint8_t cos; 206 char *next_action_name; 207 ipp_action_id_t next_action; 208 dlcosmk_data_t *dlcosmk_data; 209 uint32_t bstats; 210 211 ASSERT((nvlpp != NULL) && (*nvlpp != NULL)); 212 213 nvlp = *nvlpp; 214 *nvlpp = NULL; /* nvlist should be NULL when this returns */ 215 216 if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type)) 217 != 0) { 218 nvlist_free(nvlp); 219 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\ 220 "type\n")); 221 return (err); 222 } 223 224 if (config_type != IPP_SET) { 225 nvlist_free(nvlp); 226 dlcosmk0dbg(("dlcosmk_modify_action: invalid configuration "\ 227 "type %d\n", config_type)); 228 return (EINVAL); 229 } 230 231 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 232 ASSERT(dlcosmk_data != NULL); 233 234 /* parse next action name, if present */ 235 if ((err = nvlist_lookup_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 236 &next_action_name)) == 0) { 237 /* lookup action name to get action id */ 238 if ((next_action = ipp_action_lookup(next_action_name)) 239 == IPP_ACTION_INVAL) { 240 nvlist_free(nvlp); 241 dlcosmk0dbg(("dlcosmk_modify_action: next_action "\ 242 "invalid\n")); 243 return (EINVAL); 244 } 245 /* reference new action */ 246 if ((err = ipp_action_ref(aid, next_action, flags)) != 0) { 247 nvlist_free(nvlp); 248 dlcosmk0dbg(("dlcosmk_modify_action: ipp_action_ref "\ 249 "returned with error %d\n", err)); 250 return (err); 251 } 252 /* unref old action */ 253 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags); 254 ASSERT(err == 0); 255 dlcosmk_data->next_action = next_action; 256 } 257 258 /* parse cos, if present */ 259 if ((err = nvlist_lookup_byte(nvlp, DLCOSMK_COS, &cos)) == 0) { 260 261 /* parse b_band, mapped from cos */ 262 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_BAND, 263 &band)) != 0) { 264 nvlist_free(nvlp); 265 dlcosmk0dbg(("dlcosmk_modify_action: b_band not "\ 266 "provided\n")); 267 return (err); 268 } 269 270 /* parse dl_priority, mapped from cos */ 271 if ((err = nvlist_lookup_uint32(nvlp, DLCOSMK_PRI, 272 &dlpri)) != 0) { 273 nvlist_free(nvlp); 274 dlcosmk0dbg(("dlcosmk_modify_action: dl_priority not "\ 275 "provided\n")); 276 return (err); 277 } 278 279 /* Have all the three values, change them */ 280 dlcosmk_data->usr_pri = cos; 281 dlcosmk_data->b_band = band; 282 dlcosmk_data->dl_max = dlpri; 283 } 284 285 286 /* parse gather_stats boolean, if present */ 287 if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats)) 288 == 0) { 289 boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE; 290 /* Turning on stats */ 291 if (!dlcosmk_data->gather_stats && val) { 292 if ((err = dlcosmk_statinit(aid, dlcosmk_data)) != 0) { 293 nvlist_free(nvlp); 294 return (err); 295 } 296 /* Turning off stats */ 297 } else if (!val && dlcosmk_data->gather_stats) { 298 ipp_stat_destroy(dlcosmk_data->stats); 299 300 } 301 dlcosmk_data->gather_stats = val; 302 } 303 304 /* Free thenvlist */ 305 nvlist_free(nvlp); 306 return (0); 307 } 308 309 static int 310 dlcosmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags) 311 { 312 dlcosmk_data_t *dlcosmk_data; 313 int err; 314 315 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 316 ASSERT(dlcosmk_data != NULL); 317 318 /* Destroy stats, if gathered */ 319 if (dlcosmk_data->gather_stats) { 320 ipp_stat_destroy(dlcosmk_data->stats); 321 } 322 323 /* unreference the action */ 324 err = ipp_action_unref(aid, dlcosmk_data->next_action, flags); 325 ASSERT(err == 0); 326 327 kmem_free(dlcosmk_data, DLCOSMK_DATA_SZ); 328 return (0); 329 } 330 331 static int 332 dlcosmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet) 333 { 334 dlcosmk_data_t *dlcosmk_data; 335 mblk_t *mp = NULL; 336 int err; 337 ip_priv_t *priv; 338 339 ASSERT(packet != NULL); 340 341 /* get mblk from ipp_packet structure */ 342 mp = ipp_packet_get_data(packet); 343 priv = (ip_priv_t *)ipp_packet_get_private(packet); 344 345 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 346 ASSERT(dlcosmk_data != NULL); 347 348 /* dlcosmk packet as configured */ 349 if ((err = dlcosmk_process(&mp, dlcosmk_data, priv->ill_index, 350 priv->proc)) != 0) { 351 return (err); 352 } else { 353 /* return packet with next action set */ 354 return (ipp_packet_next(packet, dlcosmk_data->next_action)); 355 } 356 } 357 358 static int 359 dlcosmk_statinit(ipp_action_id_t aid, dlcosmk_data_t *dlcosmk_data) 360 { 361 int err; 362 dlcosmk_stat_t *statp; 363 364 /* install stats entry */ 365 if ((err = ipp_stat_create(aid, DLCOSMK_STATS_STRING, 366 DLCOSMK_STATS_COUNT, dlcosmk_update_stats, dlcosmk_data, 367 &dlcosmk_data->stats)) != 0) { 368 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_create " \ 369 "returned with error %d\n", err)); 370 return (err); 371 } 372 373 statp = (dlcosmk_stat_t *)(dlcosmk_data->stats)->ipps_data; 374 ASSERT(statp != NULL); 375 376 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "npackets", 377 IPP_STAT_UINT64, &statp->npackets)) != 0) { 378 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 379 "returned with error %d\n", err)); 380 return (err); 381 } 382 383 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "ipackets", 384 IPP_STAT_UINT64, &statp->ipackets)) != 0) { 385 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 386 "returned with error %d\n", err)); 387 return (err); 388 } 389 390 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "epackets", 391 IPP_STAT_UINT64, &statp->epackets)) != 0) { 392 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 393 "returned with error %d\n", err)); 394 return (err); 395 } 396 397 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "usr_pri", 398 IPP_STAT_INT32, &statp->usr_pri)) != 0) { 399 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 400 "returned with error %d", err)); 401 return (err); 402 } 403 404 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "b_band", 405 IPP_STAT_INT32, &statp->b_band)) != 0) { 406 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 407 "returned with error %d\n", err)); 408 return (err); 409 } 410 411 if ((err = ipp_stat_named_init(dlcosmk_data->stats, "dl_max", 412 IPP_STAT_INT32, &statp->dl_max)) != 0) { 413 dlcosmk0dbg(("dlcosmk_create_action: ipp_stat_named_init " \ 414 "returned with error %d\n", err)); 415 return (err); 416 } 417 418 ipp_stat_install(dlcosmk_data->stats); 419 return (0); 420 } 421 422 static int 423 dlcosmk_update_stats(ipp_stat_t *sp, void *arg, int rw) 424 { 425 dlcosmk_data_t *dlcosmk_data = (dlcosmk_data_t *)arg; 426 dlcosmk_stat_t *snames = (dlcosmk_stat_t *)sp->ipps_data; 427 uint32_t upri, bband; 428 429 ASSERT(dlcosmk_data != NULL); 430 ASSERT(snames != NULL); 431 432 upri = dlcosmk_data->usr_pri; 433 bband = dlcosmk_data->b_band; 434 435 (void) ipp_stat_named_op(&snames->npackets, &dlcosmk_data->npackets, 436 rw); 437 (void) ipp_stat_named_op(&snames->ipackets, &dlcosmk_data->ipackets, 438 rw); 439 (void) ipp_stat_named_op(&snames->epackets, &dlcosmk_data->epackets, 440 rw); 441 (void) ipp_stat_named_op(&snames->usr_pri, &upri, rw); 442 (void) ipp_stat_named_op(&snames->b_band, &bband, rw); 443 (void) ipp_stat_named_op(&snames->dl_max, &dlcosmk_data->dl_max, rw); 444 445 return (0); 446 } 447 448 /* ARGSUSED */ 449 static int 450 dlcosmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg, 451 ipp_flags_t flags) 452 { 453 nvlist_t *nvlp; 454 dlcosmk_data_t *dlcosmk_data; 455 char *next_action; 456 int err; 457 458 ASSERT(fn != NULL); 459 460 dlcosmk_data = (dlcosmk_data_t *)ipp_action_get_ptr(aid); 461 ASSERT(dlcosmk_data != NULL); 462 463 /* allocate nvlist to be passed back */ 464 if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) { 465 dlcosmk0dbg(("dlcosmk_info: error allocating memory\n")); 466 return (err); 467 } 468 469 /* look up next action with the next action id */ 470 if ((err = ipp_action_name(dlcosmk_data->next_action, 471 &next_action)) != 0) { 472 dlcosmk0dbg(("dlcosmk_info: next action not available\n")); 473 nvlist_free(nvlp); 474 return (err); 475 } 476 477 /* add next action name */ 478 if ((err = nvlist_add_string(nvlp, DLCOSMK_NEXT_ACTION_NAME, 479 next_action)) != 0) { 480 dlcosmk0dbg(("dlcosmk_info: error adding next action\n")); 481 nvlist_free(nvlp); 482 kmem_free(next_action, (strlen(next_action) + 1)); 483 return (err); 484 } 485 486 /* free action name */ 487 kmem_free(next_action, (strlen(next_action) + 1)); 488 489 /* add config type */ 490 if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) { 491 dlcosmk0dbg(("dlcosmk_info: error adding config. type\n")); 492 nvlist_free(nvlp); 493 return (err); 494 } 495 496 /* just give the cos, since that is what is provided in the config */ 497 if ((err = nvlist_add_byte(nvlp, DLCOSMK_COS, dlcosmk_data->usr_pri)) 498 != 0) { 499 dlcosmk0dbg(("dlcosmk_info: error adding cos\n")); 500 nvlist_free(nvlp); 501 return (err); 502 } 503 504 /* add gather stats boolean */ 505 if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE, 506 (dlcosmk_data->gather_stats ? 1 : 0))) != 0) { 507 dlcosmk0dbg(("dlcosmk_info: error adding stats status\n")); 508 nvlist_free(nvlp); 509 return (err); 510 } 511 512 /* call back with nvlist */ 513 err = fn(nvlp, arg); 514 515 nvlist_free(nvlp); 516 return (err); 517 }