xref: /illumos-gate/usr/src/uts/common/io/fibre-channel/fca/oce/oce_gld.c (revision defc4c8acfa01dba1ef3c13ca0cafccfcede51c0)
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 © 2003-2011 Emulex. All rights reserved.  */
23 
24 /*
25  * Source file containing the implementation of the driver entry points
26  * and related helper functions
27  */
28 
29 #include <oce_impl.h>
30 #include <oce_ioctl.h>
31 
32 /* array of properties supported by this driver */
33 char *oce_priv_props[] = {
34 	"_tx_ring_size",
35 	"_tx_bcopy_limit",
36 	"_rx_ring_size",
37 	"_rx_bcopy_limit",
38 	NULL
39 };
40 
41 extern int pow10[];
42 
43 /* ---[ static function declarations ]----------------------------------- */
44 static int oce_set_priv_prop(struct oce_dev *dev, const char *name,
45     uint_t size, const void *val);
46 
47 static int oce_get_priv_prop(struct oce_dev *dev, const char *name,
48     uint_t size, void *val);
49 
50 /* ---[ GLD entry points ]----------------------------------------------- */
51 int
52 oce_m_start(void *arg)
53 {
54 	struct oce_dev *dev = arg;
55 	int ret;
56 
57 	mutex_enter(&dev->dev_lock);
58 
59 	if (dev->state & STATE_MAC_STARTED) {
60 		mutex_exit(&dev->dev_lock);
61 		return (0);
62 	}
63 
64 	if (dev->suspended) {
65 		mutex_exit(&dev->dev_lock);
66 		return (EIO);
67 	}
68 	ret = oce_start(dev);
69 	if (ret != DDI_SUCCESS) {
70 		mutex_exit(&dev->dev_lock);
71 		return (EIO);
72 	}
73 
74 	dev->state |= STATE_MAC_STARTED;
75 	mutex_exit(&dev->dev_lock);
76 
77 
78 	return (DDI_SUCCESS);
79 }
80 
81 int
82 oce_start(struct oce_dev *dev)
83 {
84 	int qidx = 0;
85 	struct link_status link = {0};
86 
87 	/* get link status */
88 	(void) oce_get_link_status(dev, &link);
89 
90 	dev->link_status  = (link.logical_link_status == NTWK_LOGICAL_LINK_UP) ?
91 	    LINK_STATE_UP : LINK_STATE_DOWN;
92 
93 	dev->link_speed = link.qos_link_speed ? link.qos_link_speed * 10 :
94 	    pow10[link.mac_speed];
95 
96 	mac_link_update(dev->mac_handle, dev->link_status);
97 
98 	for (qidx = 0; qidx < dev->nwqs; qidx++) {
99 		(void) oce_start_wq(dev->wq[qidx]);
100 	}
101 	for (qidx = 0; qidx < dev->nrqs; qidx++) {
102 		(void) oce_start_rq(dev->rq[qidx]);
103 	}
104 	(void) oce_start_mq(dev->mq);
105 	/* enable interrupts */
106 	oce_ei(dev);
107 	/* arm the eqs */
108 	for (qidx = 0; qidx < dev->neqs; qidx++) {
109 		oce_arm_eq(dev, dev->eq[qidx]->eq_id, 0, B_TRUE, B_FALSE);
110 	}
111 	/* TODO update state */
112 	return (DDI_SUCCESS);
113 } /* oce_start */
114 
115 
116 void
117 oce_m_stop(void *arg)
118 {
119 	struct oce_dev *dev = arg;
120 
121 	/* disable interrupts */
122 
123 	mutex_enter(&dev->dev_lock);
124 	if (dev->suspended) {
125 		mutex_exit(&dev->dev_lock);
126 		return;
127 	}
128 	dev->state |= STATE_MAC_STOPPING;
129 	oce_stop(dev);
130 	dev->state &= ~(STATE_MAC_STOPPING | STATE_MAC_STARTED);
131 	mutex_exit(&dev->dev_lock);
132 }
133 /* called with Tx/Rx comp locks held */
134 void
135 oce_stop(struct oce_dev *dev)
136 {
137 	int qidx;
138 	/* disable interrupts */
139 	oce_di(dev);
140 	for (qidx = 0; qidx < dev->nwqs; qidx++) {
141 		mutex_enter(&dev->wq[qidx]->tx_lock);
142 	}
143 	mutex_enter(&dev->mq->lock);
144 	/* complete the pending Tx */
145 	for (qidx = 0; qidx < dev->nwqs; qidx++)
146 		oce_clean_wq(dev->wq[qidx]);
147 	/* Release all the locks */
148 	mutex_exit(&dev->mq->lock);
149 	for (qidx = 0; qidx < dev->nwqs; qidx++)
150 		mutex_exit(&dev->wq[qidx]->tx_lock);
151 	if (dev->link_status == LINK_STATE_UP) {
152 		dev->link_status = LINK_STATE_UNKNOWN;
153 		mac_link_update(dev->mac_handle, dev->link_status);
154 	}
155 
156 } /* oce_stop */
157 
158 int
159 oce_m_multicast(void *arg, boolean_t add, const uint8_t *mca)
160 {
161 	struct oce_dev *dev = (struct oce_dev *)arg;
162 	struct ether_addr  *mca_drv_list;
163 	struct ether_addr  mca_hw_list[OCE_MAX_MCA];
164 	uint16_t new_mcnt = dev->num_mca;
165 	int ret;
166 	int i;
167 
168 	/* check the address */
169 	if ((mca[0] & 0x1) == 0) {
170 		return (EINVAL);
171 	}
172 	/* Allocate the local array for holding the addresses temporarily */
173 	bzero(&mca_hw_list, sizeof (&mca_hw_list));
174 	mca_drv_list = &dev->multi_cast[0];
175 
176 	DEV_LOCK(dev);
177 	if (add) {
178 		/* check if we exceeded hw max  supported */
179 		if (new_mcnt < OCE_MAX_MCA) {
180 			/* copy entire dev mca to the mbx */
181 			bcopy((void*)mca_drv_list,
182 			    (void*)mca_hw_list,
183 			    (dev->num_mca * sizeof (struct ether_addr)));
184 			/* Append the new one to local list */
185 			bcopy(mca, &mca_hw_list[dev->num_mca],
186 			    sizeof (struct ether_addr));
187 		}
188 		new_mcnt++;
189 	} else {
190 		struct ether_addr *hwlistp = &mca_hw_list[0];
191 		for (i = 0; i < dev->num_mca; i++) {
192 			/* copy only if it does not match */
193 			if (bcmp((mca_drv_list + i), mca, ETHERADDRL)) {
194 				bcopy(mca_drv_list + i, hwlistp,
195 				    ETHERADDRL);
196 				hwlistp++;
197 			} else {
198 				new_mcnt--;
199 			}
200 		}
201 	}
202 
203 	if (dev->suspended) {
204 		goto finish;
205 	}
206 	if (new_mcnt > OCE_MAX_MCA) {
207 		ret = oce_set_multicast_table(dev, dev->if_id, &mca_hw_list[0],
208 		    OCE_MAX_MCA, B_TRUE);
209 	} else {
210 		ret = oce_set_multicast_table(dev, dev->if_id,
211 		    &mca_hw_list[0], new_mcnt, B_FALSE);
212 	}
213 		if (ret != 0) {
214 		oce_log(dev, CE_WARN, MOD_CONFIG,
215 		    "mcast %s fails", add ? "ADD" : "DEL");
216 		DEV_UNLOCK(dev);
217 		return (EIO);
218 	}
219 	/*
220 	 *  Copy the local structure to dev structure
221 	 */
222 finish:
223 	if (new_mcnt && new_mcnt <= OCE_MAX_MCA) {
224 		bcopy(mca_hw_list, mca_drv_list,
225 		    new_mcnt * sizeof (struct ether_addr));
226 
227 		dev->num_mca = (uint16_t)new_mcnt;
228 	}
229 	DEV_UNLOCK(dev);
230 	oce_log(dev, CE_NOTE, MOD_CONFIG,
231 	    "mcast %s, addr=%02x:%02x:%02x:%02x:%02x:%02x, num_mca=%d",
232 	    add ? "ADD" : "DEL",
233 	    mca[0], mca[1], mca[2], mca[3], mca[4], mca[5],
234 	    dev->num_mca);
235 	return (0);
236 } /* oce_m_multicast */
237 
238 int
239 oce_m_unicast(void *arg, const uint8_t *uca)
240 {
241 	struct oce_dev *dev = arg;
242 	int ret;
243 
244 	DEV_LOCK(dev);
245 	if (dev->suspended) {
246 		bcopy(uca, dev->unicast_addr, ETHERADDRL);
247 		dev->num_smac = 0;
248 		DEV_UNLOCK(dev);
249 		return (DDI_SUCCESS);
250 	}
251 
252 	/* Delete previous one and add new one */
253 	ret = oce_del_mac(dev, dev->if_id, &dev->pmac_id);
254 	if (ret != DDI_SUCCESS) {
255 		DEV_UNLOCK(dev);
256 		return (EIO);
257 	}
258 	dev->num_smac = 0;
259 	bzero(dev->unicast_addr, ETHERADDRL);
260 
261 	/* Set the New MAC addr earlier is no longer valid */
262 	ret = oce_add_mac(dev, dev->if_id, uca, &dev->pmac_id);
263 	if (ret != DDI_SUCCESS) {
264 		DEV_UNLOCK(dev);
265 		return (EIO);
266 	}
267 	bcopy(uca, dev->unicast_addr, ETHERADDRL);
268 	dev->num_smac = 1;
269 	DEV_UNLOCK(dev);
270 	return (ret);
271 } /* oce_m_unicast */
272 
273 /*
274  * Hashing policy for load balancing over the set of TX rings
275  * available to the driver.
276  */
277 mblk_t *
278 oce_m_send(void *arg, mblk_t *mp)
279 {
280 	struct oce_dev *dev = arg;
281 	mblk_t *nxt_pkt;
282 	mblk_t *rmp = NULL;
283 	struct oce_wq *wq;
284 
285 	DEV_LOCK(dev);
286 	if (dev->suspended || !(dev->state & STATE_MAC_STARTED)) {
287 		DEV_UNLOCK(dev);
288 		freemsg(mp);
289 		return (NULL);
290 	}
291 	DEV_UNLOCK(dev);
292 	/*
293 	 * Hash to pick a wq
294 	 */
295 	wq = oce_get_wq(dev, mp);
296 
297 	while (mp != NULL) {
298 		/* Save the Pointer since mp will be freed in case of copy */
299 		nxt_pkt = mp->b_next;
300 		mp->b_next = NULL;
301 		/* Hardcode wq since we have only one */
302 		rmp = oce_send_packet(wq, mp);
303 		if (rmp != NULL) {
304 			/* reschedule Tx */
305 			wq->resched = B_TRUE;
306 			oce_arm_cq(dev, wq->cq->cq_id, 0, B_TRUE);
307 			/* restore the chain */
308 			rmp->b_next = nxt_pkt;
309 			break;
310 		}
311 		mp  = nxt_pkt;
312 	}
313 	return (rmp);
314 } /* oce_send */
315 
316 boolean_t
317 oce_m_getcap(void *arg, mac_capab_t cap, void *data)
318 {
319 	struct oce_dev *dev = arg;
320 	boolean_t ret = B_TRUE;
321 	switch (cap) {
322 
323 	case MAC_CAPAB_HCKSUM: {
324 		uint32_t *csum_flags = u32ptr(data);
325 		*csum_flags = HCKSUM_ENABLE |
326 		    HCKSUM_INET_FULL_V4 |
327 		    HCKSUM_IPHDRCKSUM;
328 		break;
329 	}
330 	case MAC_CAPAB_LSO: {
331 		mac_capab_lso_t *mcap_lso = (mac_capab_lso_t *)data;
332 		if (dev->lso_capable) {
333 			mcap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
334 			mcap_lso->lso_basic_tcp_ipv4.lso_max = OCE_LSO_MAX_SIZE;
335 		} else {
336 			ret = B_FALSE;
337 		}
338 		break;
339 	}
340 	default:
341 		ret = B_FALSE;
342 		break;
343 	}
344 	return (ret);
345 } /* oce_m_getcap */
346 
347 int
348 oce_m_setprop(void *arg, const char *name, mac_prop_id_t id,
349     uint_t size, const void *val)
350 {
351 	struct oce_dev *dev = arg;
352 	int ret = 0;
353 
354 	DEV_LOCK(dev);
355 	switch (id) {
356 	case MAC_PROP_MTU: {
357 		uint32_t mtu;
358 
359 		bcopy(val, &mtu, sizeof (uint32_t));
360 
361 		if (dev->mtu == mtu) {
362 			ret = 0;
363 			break;
364 		}
365 
366 		if (mtu != OCE_MIN_MTU && mtu != OCE_MAX_MTU) {
367 			ret = EINVAL;
368 			break;
369 		}
370 
371 		ret = mac_maxsdu_update(dev->mac_handle, mtu);
372 		if (0 == ret) {
373 			dev->mtu = mtu;
374 			break;
375 		}
376 		break;
377 	}
378 
379 	case MAC_PROP_FLOWCTRL: {
380 		link_flowctrl_t flowctrl;
381 		uint32_t fc = 0;
382 
383 		bcopy(val, &flowctrl, sizeof (link_flowctrl_t));
384 
385 		switch (flowctrl) {
386 		case LINK_FLOWCTRL_NONE:
387 			fc = 0;
388 			break;
389 
390 		case LINK_FLOWCTRL_RX:
391 			fc = OCE_FC_RX;
392 			break;
393 
394 		case LINK_FLOWCTRL_TX:
395 			fc = OCE_FC_TX;
396 			break;
397 
398 		case LINK_FLOWCTRL_BI:
399 			fc = OCE_FC_RX | OCE_FC_TX;
400 			break;
401 		default:
402 			ret = EINVAL;
403 			break;
404 		} /* switch flowctrl */
405 
406 		if (ret)
407 			break;
408 
409 		if (fc == dev->flow_control)
410 			break;
411 
412 		if (dev->suspended) {
413 			dev->flow_control = fc;
414 			break;
415 		}
416 		/* call to set flow control */
417 		ret = oce_set_flow_control(dev, fc);
418 		/* store the new fc setting on success */
419 		if (ret == 0) {
420 		dev->flow_control = fc;
421 		}
422 		break;
423 	}
424 
425 	case MAC_PROP_PRIVATE:
426 		ret = oce_set_priv_prop(dev, name, size, val);
427 		break;
428 
429 	default:
430 		ret = ENOTSUP;
431 		break;
432 	} /* switch id */
433 
434 	DEV_UNLOCK(dev);
435 	return (ret);
436 } /* oce_m_setprop */
437 
438 int
439 oce_m_getprop(void *arg, const char *name, mac_prop_id_t id,
440     uint_t size, void *val)
441 {
442 	struct oce_dev *dev = arg;
443 	uint32_t ret = 0;
444 
445 	switch (id) {
446 	case MAC_PROP_ADV_10GFDX_CAP:
447 	case MAC_PROP_EN_10GFDX_CAP:
448 		*(uint8_t *)val = 0x01;
449 		break;
450 
451 	case MAC_PROP_DUPLEX: {
452 		uint32_t *mode = (uint32_t *)val;
453 
454 		ASSERT(size >= sizeof (link_duplex_t));
455 		if (dev->state & STATE_MAC_STARTED)
456 			*mode = LINK_DUPLEX_FULL;
457 		else
458 			*mode = LINK_DUPLEX_UNKNOWN;
459 		break;
460 	}
461 
462 	case MAC_PROP_SPEED: {
463 		uint64_t *speed = (uint64_t *)val;
464 		struct link_status link = {0};
465 
466 		ASSERT(size >= sizeof (uint64_t));
467 		*speed = 0;
468 
469 		if (dev->state & STATE_MAC_STARTED) {
470 			if (dev->link_speed < 0) {
471 				(void) oce_get_link_status(dev, &link);
472 				dev->link_speed = link.qos_link_speed ?
473 				    link.qos_link_speed * 10 :
474 				    pow10[link.mac_speed];
475 			}
476 
477 			*speed = dev->link_speed * 1000000ull;
478 		}
479 		break;
480 	}
481 
482 	case MAC_PROP_FLOWCTRL: {
483 		link_flowctrl_t *fc = (link_flowctrl_t *)val;
484 
485 		ASSERT(size >= sizeof (link_flowctrl_t));
486 		if (dev->flow_control & OCE_FC_TX &&
487 		    dev->flow_control & OCE_FC_RX)
488 			*fc = LINK_FLOWCTRL_BI;
489 		else if (dev->flow_control == OCE_FC_TX)
490 			*fc = LINK_FLOWCTRL_TX;
491 		else if (dev->flow_control == OCE_FC_RX)
492 			*fc = LINK_FLOWCTRL_RX;
493 		else if (dev->flow_control == 0)
494 			*fc = LINK_FLOWCTRL_NONE;
495 		else
496 			ret = EINVAL;
497 		break;
498 	}
499 
500 	case MAC_PROP_PRIVATE:
501 		ret = oce_get_priv_prop(dev, name, size, val);
502 		break;
503 
504 	default:
505 		ret = ENOTSUP;
506 		break;
507 	} /* switch id */
508 	return (ret);
509 } /* oce_m_getprop */
510 
511 void
512 oce_m_propinfo(void *arg, const char *name, mac_prop_id_t pr_num,
513     mac_prop_info_handle_t prh)
514 {
515 	_NOTE(ARGUNUSED(arg));
516 
517 	switch (pr_num) {
518 	case MAC_PROP_AUTONEG:
519 	case MAC_PROP_EN_AUTONEG:
520 	case MAC_PROP_ADV_1000FDX_CAP:
521 	case MAC_PROP_EN_1000FDX_CAP:
522 	case MAC_PROP_ADV_1000HDX_CAP:
523 	case MAC_PROP_EN_1000HDX_CAP:
524 	case MAC_PROP_ADV_100FDX_CAP:
525 	case MAC_PROP_EN_100FDX_CAP:
526 	case MAC_PROP_ADV_100HDX_CAP:
527 	case MAC_PROP_EN_100HDX_CAP:
528 	case MAC_PROP_ADV_10FDX_CAP:
529 	case MAC_PROP_EN_10FDX_CAP:
530 	case MAC_PROP_ADV_10HDX_CAP:
531 	case MAC_PROP_EN_10HDX_CAP:
532 	case MAC_PROP_ADV_100T4_CAP:
533 	case MAC_PROP_EN_100T4_CAP:
534 	case MAC_PROP_ADV_10GFDX_CAP:
535 	case MAC_PROP_EN_10GFDX_CAP:
536 	case MAC_PROP_SPEED:
537 	case MAC_PROP_DUPLEX:
538 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
539 		break;
540 
541 	case MAC_PROP_MTU:
542 		mac_prop_info_set_range_uint32(prh, OCE_MIN_MTU, OCE_MAX_MTU);
543 		break;
544 
545 	case MAC_PROP_PRIVATE: {
546 		char valstr[64];
547 		int value;
548 
549 		if (strcmp(name, "_tx_ring_size") == 0) {
550 			value = OCE_DEFAULT_TX_RING_SIZE;
551 		} else if (strcmp(name, "_rx_ring_size") == 0) {
552 			value = OCE_DEFAULT_RX_RING_SIZE;
553 		} else {
554 			return;
555 		}
556 
557 		(void) snprintf(valstr, sizeof (valstr), "%d", value);
558 		mac_prop_info_set_default_str(prh, valstr);
559 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
560 		break;
561 	}
562 	}
563 } /* oce_m_propinfo */
564 
565 /*
566  * function to handle dlpi streams message from GLDv3 mac layer
567  */
568 void
569 oce_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
570 {
571 	struct oce_dev *dev = arg;
572 	struct  iocblk *iocp;
573 	int cmd;
574 	uint32_t payload_length;
575 	int ret;
576 
577 	iocp = (struct iocblk *)voidptr(mp->b_rptr);
578 	iocp->ioc_error = 0;
579 	cmd = iocp->ioc_cmd;
580 
581 	DEV_LOCK(dev);
582 	if (dev->suspended) {
583 		miocnak(wq, mp, 0, EINVAL);
584 		DEV_UNLOCK(dev);
585 		return;
586 	}
587 	DEV_UNLOCK(dev);
588 
589 	switch (cmd) {
590 
591 	case OCE_ISSUE_MBOX: {
592 		ret = oce_issue_mbox(dev, wq, mp, &payload_length);
593 		miocack(wq, mp, payload_length, ret);
594 		break;
595 	}
596 	case OCE_QUERY_DRIVER_DATA: {
597 		struct oce_driver_query *drv_query =
598 		    (struct oce_driver_query *)(void *)mp->b_cont->b_rptr;
599 
600 		/* if the driver version does not match bail */
601 		if (drv_query->version != OCN_VERSION_SUPPORTED) {
602 			oce_log(dev, CE_NOTE, MOD_CONFIG, "%s",
603 			    "One Connect version mismatch");
604 			miocnak(wq, mp, 0, ENOTSUP);
605 			break;
606 		}
607 
608 		/* fill the return values */
609 		bcopy(OCE_MOD_NAME, drv_query->driver_name,
610 		    (sizeof (OCE_MOD_NAME) > 32) ?
611 		    31 : sizeof (OCE_MOD_NAME));
612 		drv_query->driver_name[31] = '\0';
613 
614 		bcopy(OCE_VERSION, drv_query->driver_version,
615 		    (sizeof (OCE_VERSION) > 32) ? 31 :
616 		    sizeof (OCE_VERSION));
617 		drv_query->driver_version[31] = '\0';
618 
619 		if (dev->num_smac == 0) {
620 			drv_query->num_smac = 1;
621 			bcopy(dev->mac_addr, drv_query->smac_addr[0],
622 			    ETHERADDRL);
623 		} else {
624 			drv_query->num_smac = dev->num_smac;
625 			bcopy(dev->unicast_addr, drv_query->smac_addr[0],
626 			    ETHERADDRL);
627 		}
628 
629 		bcopy(dev->mac_addr, drv_query->pmac_addr, ETHERADDRL);
630 
631 		payload_length = sizeof (struct oce_driver_query);
632 		miocack(wq, mp, payload_length, 0);
633 		break;
634 	}
635 
636 	default:
637 		miocnak(wq, mp, 0, ENOTSUP);
638 		break;
639 	}
640 } /* oce_m_ioctl */
641 
642 int
643 oce_m_promiscuous(void *arg, boolean_t enable)
644 {
645 	struct oce_dev *dev = arg;
646 	int ret = 0;
647 
648 	DEV_LOCK(dev);
649 
650 	if (dev->promisc == enable) {
651 		DEV_UNLOCK(dev);
652 		return (ret);
653 	}
654 
655 	if (dev->suspended) {
656 		/* remember the setting */
657 		dev->promisc = enable;
658 		DEV_UNLOCK(dev);
659 		return (ret);
660 	}
661 
662 	ret = oce_set_promiscuous(dev, enable);
663 	if (ret == DDI_SUCCESS)
664 		dev->promisc = enable;
665 	DEV_UNLOCK(dev);
666 	return (ret);
667 } /* oce_m_promiscuous */
668 
669 /*
670  * function to set a private property.
671  * Called from the set_prop GLD entry point
672  *
673  * dev - sofware handle to the device
674  * name - string containing the property name
675  * size - length of the string in name
676  * val - pointer to a location where the value to set is stored
677  *
678  * return EINVAL => invalid value in val 0 => success
679  */
680 static int
681 oce_set_priv_prop(struct oce_dev *dev, const char *name,
682     uint_t size, const void *val)
683 {
684 	int ret = ENOTSUP;
685 	long result;
686 
687 	_NOTE(ARGUNUSED(size));
688 
689 	if (NULL == val) {
690 		ret = EINVAL;
691 		return (ret);
692 	}
693 
694 	if (strcmp(name, "_tx_bcopy_limit") == 0) {
695 		(void) ddi_strtol(val, (char **)NULL, 0, &result);
696 		if (result <= OCE_WQ_BUF_SIZE) {
697 			if (result != dev->tx_bcopy_limit)
698 				dev->tx_bcopy_limit = (uint32_t)result;
699 			ret = 0;
700 		} else {
701 			ret = EINVAL;
702 		}
703 	}
704 	if (strcmp(name, "_rx_bcopy_limit") == 0) {
705 		(void) ddi_strtol(val, (char **)NULL, 0, &result);
706 		if (result <= OCE_RQ_BUF_SIZE) {
707 			if (result != dev->rx_bcopy_limit)
708 				dev->rx_bcopy_limit = (uint32_t)result;
709 			ret = 0;
710 		} else {
711 			ret = EINVAL;
712 		}
713 	}
714 
715 	return (ret);
716 } /* oce_set_priv_prop */
717 
718 /*
719  * function to get the value of a private property. Called from get_prop
720  *
721  * dev - software handle to the device
722  * name - string containing the property name
723  * size - length of the string contained name
724  * val - [OUT] pointer to the location where the result is returned
725  *
726  * return EINVAL => invalid request 0 => success
727  */
728 static int
729 oce_get_priv_prop(struct oce_dev *dev, const char *name,
730     uint_t size, void *val)
731 {
732 	int value;
733 
734 	if (strcmp(name, "_tx_ring_size") == 0) {
735 		value = dev->tx_ring_size;
736 	} else if (strcmp(name, "_tx_bcopy_limit") == 0) {
737 		value = dev->tx_bcopy_limit;
738 	} else if (strcmp(name, "_rx_ring_size") == 0) {
739 		value = dev->rx_ring_size;
740 	} else if (strcmp(name, "_rx_bcopy_limit") == 0) {
741 		value = dev->rx_bcopy_limit;
742 	} else {
743 		return (ENOTSUP);
744 	}
745 
746 	(void) snprintf(val, size, "%d", value);
747 	return (0);
748 } /* oce_get_priv_prop */
749