blob: 6158daf685c28dcafb661a13268f8988ee413716 [file] [log] [blame]
Rahul Lakkireddyb72a32d2016-08-22 16:29:06 +05301/*
2 * This file is part of the Chelsio T4 Ethernet driver for Linux.
3 *
4 * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.
5 *
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:
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer.
19 *
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.
24 *
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
32 * SOFTWARE.
33 */
34
35#include <linux/module.h>
36#include <linux/netdevice.h>
37
38#include "cxgb4.h"
39#include "sched.h"
40
41/* Spinlock must be held by caller */
42static int t4_sched_class_fw_cmd(struct port_info *pi,
43 struct ch_sched_params *p,
44 enum sched_fw_ops op)
45{
46 struct adapter *adap = pi->adapter;
47 struct sched_table *s = pi->sched_tbl;
48 struct sched_class *e;
49 int err = 0;
50
51 e = &s->tab[p->u.params.class];
52 switch (op) {
53 case SCHED_FW_OP_ADD:
54 err = t4_sched_params(adap, p->type,
55 p->u.params.level, p->u.params.mode,
56 p->u.params.rateunit,
57 p->u.params.ratemode,
58 p->u.params.channel, e->idx,
59 p->u.params.minrate, p->u.params.maxrate,
60 p->u.params.weight, p->u.params.pktsize);
61 break;
62 default:
63 err = -ENOTSUPP;
64 break;
65 }
66
67 return err;
68}
69
70/* If @p is NULL, fetch any available unused class */
71static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
72 const struct ch_sched_params *p)
73{
74 struct sched_table *s = pi->sched_tbl;
75 struct sched_class *e, *end;
76 struct sched_class *found = NULL;
77
78 if (!p) {
79 /* Get any available unused class */
80 end = &s->tab[s->sched_size];
81 for (e = &s->tab[0]; e != end; ++e) {
82 if (e->state == SCHED_STATE_UNUSED) {
83 found = e;
84 break;
85 }
86 }
87 } else {
88 /* Look for a class with matching scheduling parameters */
89 struct ch_sched_params info;
90 struct ch_sched_params tp;
91
92 memset(&info, 0, sizeof(info));
93 memset(&tp, 0, sizeof(tp));
94
95 memcpy(&tp, p, sizeof(tp));
96 /* Don't try to match class parameter */
97 tp.u.params.class = SCHED_CLS_NONE;
98
99 end = &s->tab[s->sched_size];
100 for (e = &s->tab[0]; e != end; ++e) {
101 if (e->state == SCHED_STATE_UNUSED)
102 continue;
103
104 memset(&info, 0, sizeof(info));
105 memcpy(&info, &e->info, sizeof(info));
106 /* Don't try to match class parameter */
107 info.u.params.class = SCHED_CLS_NONE;
108
109 if ((info.type == tp.type) &&
110 (!memcmp(&info.u.params, &tp.u.params,
111 sizeof(info.u.params)))) {
112 found = e;
113 break;
114 }
115 }
116 }
117
118 return found;
119}
120
121static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
122 struct ch_sched_params *p)
123{
124 struct sched_table *s = pi->sched_tbl;
125 struct sched_class *e;
126 u8 class_id;
127 int err;
128
129 if (!p)
130 return NULL;
131
132 class_id = p->u.params.class;
133
134 /* Only accept search for existing class with matching params
135 * or allocation of new class with specified params
136 */
137 if (class_id != SCHED_CLS_NONE)
138 return NULL;
139
140 write_lock(&s->rw_lock);
141 /* See if there's an exisiting class with same
142 * requested sched params
143 */
144 e = t4_sched_class_lookup(pi, p);
145 if (!e) {
146 struct ch_sched_params np;
147
148 /* Fetch any available unused class */
149 e = t4_sched_class_lookup(pi, NULL);
150 if (!e)
151 goto out;
152
153 memset(&np, 0, sizeof(np));
154 memcpy(&np, p, sizeof(np));
155 np.u.params.class = e->idx;
156
157 spin_lock(&e->lock);
158 /* New class */
159 err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD);
160 if (err) {
161 spin_unlock(&e->lock);
162 e = NULL;
163 goto out;
164 }
165 memcpy(&e->info, &np, sizeof(e->info));
166 atomic_set(&e->refcnt, 0);
167 e->state = SCHED_STATE_ACTIVE;
168 spin_unlock(&e->lock);
169 }
170
171out:
172 write_unlock(&s->rw_lock);
173 return e;
174}
175
176/**
177 * cxgb4_sched_class_alloc - allocate a scheduling class
178 * @dev: net_device pointer
179 * @p: new scheduling class to create.
180 *
181 * Returns pointer to the scheduling class created. If @p is NULL, then
182 * it allocates and returns any available unused scheduling class. If a
183 * scheduling class with matching @p is found, then the matching class is
184 * returned.
185 */
186struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
187 struct ch_sched_params *p)
188{
189 struct port_info *pi = netdev2pinfo(dev);
190 u8 class_id;
191
192 if (!can_sched(dev))
193 return NULL;
194
195 class_id = p->u.params.class;
196 if (!valid_class_id(dev, class_id))
197 return NULL;
198
199 return t4_sched_class_alloc(pi, p);
200}
201
202struct sched_table *t4_init_sched(unsigned int sched_size)
203{
204 struct sched_table *s;
205 unsigned int i;
206
207 s = t4_alloc_mem(sizeof(*s) + sched_size * sizeof(struct sched_class));
208 if (!s)
209 return NULL;
210
211 s->sched_size = sched_size;
212 rwlock_init(&s->rw_lock);
213
214 for (i = 0; i < s->sched_size; i++) {
215 memset(&s->tab[i], 0, sizeof(struct sched_class));
216 s->tab[i].idx = i;
217 s->tab[i].state = SCHED_STATE_UNUSED;
218 spin_lock_init(&s->tab[i].lock);
219 atomic_set(&s->tab[i].refcnt, 0);
220 }
221 return s;
222}
223
224void t4_cleanup_sched(struct adapter *adap)
225{
226 struct sched_table *s;
227 unsigned int i;
228
229 for_each_port(adap, i) {
230 struct port_info *pi = netdev2pinfo(adap->port[i]);
231
232 s = pi->sched_tbl;
233 t4_free_mem(s);
234 }
235}