2 * This file is part of the Chelsio T4 Ethernet driver for Linux.
4 * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
6 * This software is available to you under a choice of one of two
7 * licenses. You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
16 * - Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials
23 * provided with the distribution.
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 #include <linux/module.h>
36 #include <linux/netdevice.h>
41 static int t4_sched_class_fw_cmd(struct port_info
*pi
,
42 struct ch_sched_params
*p
,
45 struct adapter
*adap
= pi
->adapter
;
46 struct sched_table
*s
= pi
->sched_tbl
;
47 struct sched_class
*e
;
50 e
= &s
->tab
[p
->u
.params
.class];
53 err
= t4_sched_params(adap
, p
->type
,
54 p
->u
.params
.level
, p
->u
.params
.mode
,
57 p
->u
.params
.channel
, e
->idx
,
58 p
->u
.params
.minrate
, p
->u
.params
.maxrate
,
59 p
->u
.params
.weight
, p
->u
.params
.pktsize
);
69 static int t4_sched_bind_unbind_op(struct port_info
*pi
, void *arg
,
70 enum sched_bind_type type
, bool bind
)
72 struct adapter
*adap
= pi
->adapter
;
73 u32 fw_mnem
, fw_class
, fw_param
;
74 unsigned int pf
= adap
->pf
;
80 struct sched_queue_entry
*qe
;
82 qe
= (struct sched_queue_entry
*)arg
;
84 /* Create a template for the FW_PARAMS_CMD mnemonic and
85 * value (TX Scheduling Class in this case).
87 fw_mnem
= (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ
) |
89 FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH
));
90 fw_class
= bind
? qe
->param
.class : FW_SCHED_CLS_NONE
;
91 fw_param
= (fw_mnem
| FW_PARAMS_PARAM_YZ_V(qe
->cntxt_id
));
102 err
= t4_set_params(adap
, adap
->mbox
, pf
, vf
, 1, &fw_param
, &fw_class
);
108 static struct sched_class
*t4_sched_queue_lookup(struct port_info
*pi
,
109 const unsigned int qid
,
112 struct sched_table
*s
= pi
->sched_tbl
;
113 struct sched_class
*e
, *end
;
114 struct sched_class
*found
= NULL
;
117 /* Look for a class with matching bound queue parameters */
118 end
= &s
->tab
[s
->sched_size
];
119 for (e
= &s
->tab
[0]; e
!= end
; ++e
) {
120 struct sched_queue_entry
*qe
;
123 if (e
->state
== SCHED_STATE_UNUSED
)
126 list_for_each_entry(qe
, &e
->queue_list
, list
) {
127 if (qe
->cntxt_id
== qid
) {
143 static int t4_sched_queue_unbind(struct port_info
*pi
, struct ch_sched_queue
*p
)
145 struct adapter
*adap
= pi
->adapter
;
146 struct sched_class
*e
;
147 struct sched_queue_entry
*qe
= NULL
;
148 struct sge_eth_txq
*txq
;
153 if (p
->queue
< 0 || p
->queue
>= pi
->nqsets
)
156 txq
= &adap
->sge
.ethtxq
[pi
->first_qset
+ p
->queue
];
157 qid
= txq
->q
.cntxt_id
;
159 /* Find the existing class that the queue is bound to */
160 e
= t4_sched_queue_lookup(pi
, qid
, &index
);
161 if (e
&& index
>= 0) {
164 list_for_each_entry(qe
, &e
->queue_list
, list
) {
169 err
= t4_sched_bind_unbind_op(pi
, (void *)qe
, SCHED_QUEUE
,
176 if (atomic_dec_and_test(&e
->refcnt
)) {
177 e
->state
= SCHED_STATE_UNUSED
;
178 memset(&e
->info
, 0, sizeof(e
->info
));
184 static int t4_sched_queue_bind(struct port_info
*pi
, struct ch_sched_queue
*p
)
186 struct adapter
*adap
= pi
->adapter
;
187 struct sched_table
*s
= pi
->sched_tbl
;
188 struct sched_class
*e
;
189 struct sched_queue_entry
*qe
= NULL
;
190 struct sge_eth_txq
*txq
;
194 if (p
->queue
< 0 || p
->queue
>= pi
->nqsets
)
197 qe
= kvzalloc(sizeof(struct sched_queue_entry
), GFP_KERNEL
);
201 txq
= &adap
->sge
.ethtxq
[pi
->first_qset
+ p
->queue
];
202 qid
= txq
->q
.cntxt_id
;
204 /* Unbind queue from any existing class */
205 err
= t4_sched_queue_unbind(pi
, p
);
209 /* Bind queue to specified class */
211 memcpy(&qe
->param
, p
, sizeof(qe
->param
));
213 e
= &s
->tab
[qe
->param
.class];
214 err
= t4_sched_bind_unbind_op(pi
, (void *)qe
, SCHED_QUEUE
, true);
218 list_add_tail(&qe
->list
, &e
->queue_list
);
219 atomic_inc(&e
->refcnt
);
227 static void t4_sched_class_unbind_all(struct port_info
*pi
,
228 struct sched_class
*e
,
229 enum sched_bind_type type
)
236 struct sched_queue_entry
*qe
;
238 list_for_each_entry(qe
, &e
->queue_list
, list
)
239 t4_sched_queue_unbind(pi
, &qe
->param
);
247 static int t4_sched_class_bind_unbind_op(struct port_info
*pi
, void *arg
,
248 enum sched_bind_type type
, bool bind
)
257 struct ch_sched_queue
*qe
= (struct ch_sched_queue
*)arg
;
260 err
= t4_sched_queue_bind(pi
, qe
);
262 err
= t4_sched_queue_unbind(pi
, qe
);
274 * cxgb4_sched_class_bind - Bind an entity to a scheduling class
275 * @dev: net_device pointer
276 * @arg: Entity opaque data
277 * @type: Entity type (Queue)
279 * Binds an entity (queue) to a scheduling class. If the entity
280 * is bound to another class, it will be unbound from the other class
281 * and bound to the class specified in @arg.
283 int cxgb4_sched_class_bind(struct net_device
*dev
, void *arg
,
284 enum sched_bind_type type
)
286 struct port_info
*pi
= netdev2pinfo(dev
);
297 struct ch_sched_queue
*qe
= (struct ch_sched_queue
*)arg
;
299 class_id
= qe
->class;
306 if (!valid_class_id(dev
, class_id
))
309 if (class_id
== SCHED_CLS_NONE
)
312 return t4_sched_class_bind_unbind_op(pi
, arg
, type
, true);
317 * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class
318 * @dev: net_device pointer
319 * @arg: Entity opaque data
320 * @type: Entity type (Queue)
322 * Unbinds an entity (queue) from a scheduling class.
324 int cxgb4_sched_class_unbind(struct net_device
*dev
, void *arg
,
325 enum sched_bind_type type
)
327 struct port_info
*pi
= netdev2pinfo(dev
);
338 struct ch_sched_queue
*qe
= (struct ch_sched_queue
*)arg
;
340 class_id
= qe
->class;
347 if (!valid_class_id(dev
, class_id
))
350 return t4_sched_class_bind_unbind_op(pi
, arg
, type
, false);
353 /* If @p is NULL, fetch any available unused class */
354 static struct sched_class
*t4_sched_class_lookup(struct port_info
*pi
,
355 const struct ch_sched_params
*p
)
357 struct sched_table
*s
= pi
->sched_tbl
;
358 struct sched_class
*e
, *end
;
359 struct sched_class
*found
= NULL
;
362 /* Get any available unused class */
363 end
= &s
->tab
[s
->sched_size
];
364 for (e
= &s
->tab
[0]; e
!= end
; ++e
) {
365 if (e
->state
== SCHED_STATE_UNUSED
) {
371 /* Look for a class with matching scheduling parameters */
372 struct ch_sched_params info
;
373 struct ch_sched_params tp
;
375 memcpy(&tp
, p
, sizeof(tp
));
376 /* Don't try to match class parameter */
377 tp
.u
.params
.class = SCHED_CLS_NONE
;
379 end
= &s
->tab
[s
->sched_size
];
380 for (e
= &s
->tab
[0]; e
!= end
; ++e
) {
381 if (e
->state
== SCHED_STATE_UNUSED
)
384 memcpy(&info
, &e
->info
, sizeof(info
));
385 /* Don't try to match class parameter */
386 info
.u
.params
.class = SCHED_CLS_NONE
;
388 if ((info
.type
== tp
.type
) &&
389 (!memcmp(&info
.u
.params
, &tp
.u
.params
,
390 sizeof(info
.u
.params
)))) {
400 static struct sched_class
*t4_sched_class_alloc(struct port_info
*pi
,
401 struct ch_sched_params
*p
)
403 struct sched_class
*e
;
410 class_id
= p
->u
.params
.class;
412 /* Only accept search for existing class with matching params
413 * or allocation of new class with specified params
415 if (class_id
!= SCHED_CLS_NONE
)
418 /* See if there's an exisiting class with same
419 * requested sched params
421 e
= t4_sched_class_lookup(pi
, p
);
423 struct ch_sched_params np
;
425 /* Fetch any available unused class */
426 e
= t4_sched_class_lookup(pi
, NULL
);
430 memcpy(&np
, p
, sizeof(np
));
431 np
.u
.params
.class = e
->idx
;
433 err
= t4_sched_class_fw_cmd(pi
, &np
, SCHED_FW_OP_ADD
);
436 memcpy(&e
->info
, &np
, sizeof(e
->info
));
437 atomic_set(&e
->refcnt
, 0);
438 e
->state
= SCHED_STATE_ACTIVE
;
445 * cxgb4_sched_class_alloc - allocate a scheduling class
446 * @dev: net_device pointer
447 * @p: new scheduling class to create.
449 * Returns pointer to the scheduling class created. If @p is NULL, then
450 * it allocates and returns any available unused scheduling class. If a
451 * scheduling class with matching @p is found, then the matching class is
454 struct sched_class
*cxgb4_sched_class_alloc(struct net_device
*dev
,
455 struct ch_sched_params
*p
)
457 struct port_info
*pi
= netdev2pinfo(dev
);
463 class_id
= p
->u
.params
.class;
464 if (!valid_class_id(dev
, class_id
))
467 return t4_sched_class_alloc(pi
, p
);
470 static void t4_sched_class_free(struct port_info
*pi
, struct sched_class
*e
)
472 t4_sched_class_unbind_all(pi
, e
, SCHED_QUEUE
);
475 struct sched_table
*t4_init_sched(unsigned int sched_size
)
477 struct sched_table
*s
;
480 s
= kvzalloc(struct_size(s
, tab
, sched_size
), GFP_KERNEL
);
484 s
->sched_size
= sched_size
;
486 for (i
= 0; i
< s
->sched_size
; i
++) {
487 memset(&s
->tab
[i
], 0, sizeof(struct sched_class
));
489 s
->tab
[i
].state
= SCHED_STATE_UNUSED
;
490 INIT_LIST_HEAD(&s
->tab
[i
].queue_list
);
491 atomic_set(&s
->tab
[i
].refcnt
, 0);
496 void t4_cleanup_sched(struct adapter
*adap
)
498 struct sched_table
*s
;
501 for_each_port(adap
, j
) {
502 struct port_info
*pi
= netdev2pinfo(adap
->port
[j
]);
508 for (i
= 0; i
< s
->sched_size
; i
++) {
509 struct sched_class
*e
;
512 if (e
->state
== SCHED_STATE_ACTIVE
)
513 t4_sched_class_free(pi
, e
);