Whamcloud - gitweb
LU-12495 obdclass: qos penalties miscalculated
[fs/lustre-release.git] / lustre / obdclass / lu_qos.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * This file is part of Lustre, http://www.lustre.org/
24  *
25  * lustre/obdclass/lu_qos.c
26  *
27  * Lustre QoS.
28  * These are the only exported functions, they provide some generic
29  * infrastructure for object allocation QoS
30  *
31  */
32
33 #define DEBUG_SUBSYSTEM S_CLASS
34
35 #include <linux/module.h>
36 #include <linux/list.h>
37 #include <linux/random.h>
38 #include <libcfs/libcfs.h>
39 #include <libcfs/libcfs_hash.h> /* hash_long() */
40 #include <libcfs/linux/linux-mem.h>
41 #include <obd_class.h>
42 #include <obd_support.h>
43 #include <lustre_disk.h>
44 #include <lustre_fid.h>
45 #include <lu_object.h>
46
47 void lu_qos_rr_init(struct lu_qos_rr *lqr)
48 {
49         spin_lock_init(&lqr->lqr_alloc);
50         lqr->lqr_dirty = 1;
51 }
52 EXPORT_SYMBOL(lu_qos_rr_init);
53
54 /**
55  * Add a new target to Quality of Service (QoS) target table.
56  *
57  * Add a new MDT/OST target to the structure representing an OSS. Resort the
58  * list of known MDSs/OSSs by the number of MDTs/OSTs attached to each MDS/OSS.
59  * The MDS/OSS list is protected internally and no external locking is required.
60  *
61  * \param[in] qos               lu_qos data
62  * \param[in] ltd               target description
63  *
64  * \retval 0                    on success
65  * \retval -ENOMEM              on error
66  */
67 int lqos_add_tgt(struct lu_qos *qos, struct lu_tgt_desc *ltd)
68 {
69         struct lu_svr_qos *svr = NULL;
70         struct lu_svr_qos *tempsvr;
71         struct obd_export *exp = ltd->ltd_exp;
72         int found = 0;
73         __u32 id = 0;
74         int rc = 0;
75
76         ENTRY;
77
78         down_write(&qos->lq_rw_sem);
79         /*
80          * a bit hacky approach to learn NID of corresponding connection
81          * but there is no official API to access information like this
82          * with OSD API.
83          */
84         list_for_each_entry(svr, &qos->lq_svr_list, lsq_svr_list) {
85                 if (obd_uuid_equals(&svr->lsq_uuid,
86                                     &exp->exp_connection->c_remote_uuid)) {
87                         found++;
88                         break;
89                 }
90                 if (svr->lsq_id > id)
91                         id = svr->lsq_id;
92         }
93
94         if (!found) {
95                 OBD_ALLOC_PTR(svr);
96                 if (!svr)
97                         GOTO(out, rc = -ENOMEM);
98                 memcpy(&svr->lsq_uuid, &exp->exp_connection->c_remote_uuid,
99                        sizeof(svr->lsq_uuid));
100                 ++id;
101                 svr->lsq_id = id;
102         } else {
103                 /* Assume we have to move this one */
104                 list_del(&svr->lsq_svr_list);
105         }
106
107         svr->lsq_tgt_count++;
108         ltd->ltd_qos.ltq_svr = svr;
109
110         CDEBUG(D_OTHER, "add tgt %s to server %s (%d targets)\n",
111                obd_uuid2str(&ltd->ltd_uuid), obd_uuid2str(&svr->lsq_uuid),
112                svr->lsq_tgt_count);
113
114         /*
115          * Add sorted by # of tgts.  Find the first entry that we're
116          * bigger than...
117          */
118         list_for_each_entry(tempsvr, &qos->lq_svr_list, lsq_svr_list) {
119                 if (svr->lsq_tgt_count > tempsvr->lsq_tgt_count)
120                         break;
121         }
122         /*
123          * ...and add before it.  If we're the first or smallest, tempsvr
124          * points to the list head, and we add to the end.
125          */
126         list_add_tail(&svr->lsq_svr_list, &tempsvr->lsq_svr_list);
127
128         qos->lq_dirty = 1;
129         qos->lq_rr.lqr_dirty = 1;
130
131 out:
132         up_write(&qos->lq_rw_sem);
133         RETURN(rc);
134 }
135 EXPORT_SYMBOL(lqos_add_tgt);
136
137 /**
138  * Remove MDT/OST target from QoS table.
139  *
140  * Removes given MDT/OST target from QoS table and releases related
141  * MDS/OSS structure if no target remain on the MDS/OSS.
142  *
143  * \param[in] qos               lu_qos data
144  * \param[in] ltd               target description
145  *
146  * \retval 0                    on success
147  * \retval -ENOENT              if no server was found
148  */
149 int lqos_del_tgt(struct lu_qos *qos, struct lu_tgt_desc *ltd)
150 {
151         struct lu_svr_qos *svr;
152         int rc = 0;
153
154         ENTRY;
155
156         down_write(&qos->lq_rw_sem);
157         svr = ltd->ltd_qos.ltq_svr;
158         if (!svr)
159                 GOTO(out, rc = -ENOENT);
160
161         svr->lsq_tgt_count--;
162         if (svr->lsq_tgt_count == 0) {
163                 CDEBUG(D_OTHER, "removing server %s\n",
164                        obd_uuid2str(&svr->lsq_uuid));
165                 list_del(&svr->lsq_svr_list);
166                 ltd->ltd_qos.ltq_svr = NULL;
167                 OBD_FREE_PTR(svr);
168         }
169
170         qos->lq_dirty = 1;
171         qos->lq_rr.lqr_dirty = 1;
172 out:
173         up_write(&qos->lq_rw_sem);
174         RETURN(rc);
175 }
176 EXPORT_SYMBOL(lqos_del_tgt);
177
178 /**
179  * lu_prandom_u64_max - returns a pseudo-random u64 number in interval
180  * [0, ep_ro)
181  *
182  * \param[in] ep_ro     right open interval endpoint
183  *
184  * \retval a pseudo-random 64-bit number that is in interval [0, ep_ro).
185  */
186 u64 lu_prandom_u64_max(u64 ep_ro)
187 {
188         u64 rand = 0;
189
190         if (ep_ro) {
191 #if BITS_PER_LONG == 32
192                 /*
193                  * If ep_ro > 32-bit, first generate the high
194                  * 32 bits of the random number, then add in the low
195                  * 32 bits (truncated to the upper limit, if needed)
196                  */
197                 if (ep_ro > 0xffffffffULL)
198                         rand = prandom_u32_max((u32)(ep_ro >> 32)) << 32;
199
200                 if (rand == (ep_ro & 0xffffffff00000000ULL))
201                         rand |= prandom_u32_max((u32)ep_ro);
202                 else
203                         rand |= prandom_u32();
204 #else
205                 rand = ((u64)prandom_u32() << 32 | prandom_u32()) % ep_ro;
206 #endif
207         }
208
209         return rand;
210 }
211 EXPORT_SYMBOL(lu_prandom_u64_max);
212
213 static inline __u64 tgt_statfs_bavail(struct lu_tgt_desc *tgt)
214 {
215         struct obd_statfs *statfs = &tgt->ltd_statfs;
216
217         return statfs->os_bavail * statfs->os_bsize;
218 }
219
220 static inline __u64 tgt_statfs_iavail(struct lu_tgt_desc *tgt)
221 {
222         return tgt->ltd_statfs.os_ffree;
223 }
224
225 /**
226  * Calculate penalties per-tgt and per-server
227  *
228  * Re-calculate penalties when the configuration changes, active targets
229  * change and after statfs refresh (all these are reflected by lq_dirty flag).
230  * On every tgt and server: decay the penalty by half for every 8x the update
231  * interval that the device has been idle. That gives lots of time for the
232  * statfs information to be updated (which the penalty is only a proxy for),
233  * and avoids penalizing server/tgt under light load.
234  * See lqos_calc_weight() for how penalties are factored into the weight.
235  *
236  * \param[in] qos               lu_qos
237  * \param[in] ltd               lu_tgt_descs
238  * \param[in] active_tgt_nr     active tgt number
239  * \param[in] maxage            qos max age
240  * \param[in] is_mdt            MDT will count inode usage
241  *
242  * \retval 0            on success
243  * \retval -EAGAIN      the number of tgt isn't enough or all tgt spaces are
244  *                      almost the same
245  */
246 int lqos_calc_penalties(struct lu_qos *qos, struct lu_tgt_descs *ltd,
247                         __u32 active_tgt_nr, __u32 maxage, bool is_mdt)
248 {
249         struct lu_tgt_desc *tgt;
250         struct lu_svr_qos *svr;
251         __u64 ba_max, ba_min, ba;
252         __u64 ia_max, ia_min, ia = 1;
253         __u32 num_active;
254         int prio_wide;
255         time64_t now, age;
256         int rc;
257
258         ENTRY;
259
260         if (!qos->lq_dirty)
261                 GOTO(out, rc = 0);
262
263         num_active = active_tgt_nr - 1;
264         if (num_active < 1)
265                 GOTO(out, rc = -EAGAIN);
266
267         /* find bavail on each server */
268         list_for_each_entry(svr, &qos->lq_svr_list, lsq_svr_list) {
269                 svr->lsq_bavail = 0;
270                 /* if inode is not counted, set to 1 to ignore */
271                 svr->lsq_iavail = is_mdt ? 0 : 1;
272         }
273         qos->lq_active_svr_count = 0;
274
275         /*
276          * How badly user wants to select targets "widely" (not recently chosen
277          * and not on recent MDS's).  As opposed to "freely" (free space avail.)
278          * 0-256
279          */
280         prio_wide = 256 - qos->lq_prio_free;
281
282         ba_min = (__u64)(-1);
283         ba_max = 0;
284         ia_min = (__u64)(-1);
285         ia_max = 0;
286         now = ktime_get_real_seconds();
287
288         /* Calculate server penalty per object */
289         ltd_foreach_tgt(ltd, tgt) {
290                 if (!tgt->ltd_active)
291                         continue;
292
293                 /* when inode is counted, bavail >> 16 to avoid overflow */
294                 ba = tgt_statfs_bavail(tgt);
295                 if (is_mdt)
296                         ba >>= 16;
297                 else
298                         ba >>= 8;
299                 if (!ba)
300                         continue;
301
302                 ba_min = min(ba, ba_min);
303                 ba_max = max(ba, ba_max);
304
305                 /* Count the number of usable servers */
306                 if (tgt->ltd_qos.ltq_svr->lsq_bavail == 0)
307                         qos->lq_active_svr_count++;
308                 tgt->ltd_qos.ltq_svr->lsq_bavail += ba;
309
310                 if (is_mdt) {
311                         /* iavail >> 8 to avoid overflow */
312                         ia = tgt_statfs_iavail(tgt) >> 8;
313                         if (!ia)
314                                 continue;
315
316                         ia_min = min(ia, ia_min);
317                         ia_max = max(ia, ia_max);
318
319                         tgt->ltd_qos.ltq_svr->lsq_iavail += ia;
320                 }
321
322                 /*
323                  * per-tgt penalty is
324                  * prio * bavail * iavail / (num_tgt - 1) / 2
325                  */
326                 tgt->ltd_qos.ltq_penalty_per_obj = prio_wide * ba * ia >> 8;
327                 do_div(tgt->ltd_qos.ltq_penalty_per_obj, num_active);
328                 tgt->ltd_qos.ltq_penalty_per_obj >>= 1;
329
330                 age = (now - tgt->ltd_qos.ltq_used) >> 3;
331                 if (qos->lq_reset || age > 32 * maxage)
332                         tgt->ltd_qos.ltq_penalty = 0;
333                 else if (age > maxage)
334                         /* Decay tgt penalty. */
335                         tgt->ltd_qos.ltq_penalty >>= (age / maxage);
336         }
337
338         num_active = qos->lq_active_svr_count - 1;
339         if (num_active < 1) {
340                 /*
341                  * If there's only 1 server, we can't penalize it, so instead
342                  * we have to double the tgt penalty
343                  */
344                 num_active = 1;
345                 ltd_foreach_tgt(ltd, tgt) {
346                         if (!tgt->ltd_active)
347                                 continue;
348
349                         tgt->ltd_qos.ltq_penalty_per_obj <<= 1;
350                 }
351         }
352
353         /*
354          * Per-server penalty is
355          * prio * bavail * iavail / server_tgts / (num_svr - 1) / 2
356          */
357         list_for_each_entry(svr, &qos->lq_svr_list, lsq_svr_list) {
358                 ba = svr->lsq_bavail;
359                 ia = svr->lsq_iavail;
360                 svr->lsq_penalty_per_obj = prio_wide * ba  * ia >> 8;
361                 do_div(ba, svr->lsq_tgt_count * num_active);
362                 svr->lsq_penalty_per_obj >>= 1;
363
364                 age = (now - svr->lsq_used) >> 3;
365                 if (qos->lq_reset || age > 32 * maxage)
366                         svr->lsq_penalty = 0;
367                 else if (age > maxage)
368                         /* Decay server penalty. */
369                         svr->lsq_penalty >>= age / maxage;
370         }
371
372         qos->lq_dirty = 0;
373         qos->lq_reset = 0;
374
375         /*
376          * If each tgt has almost same free space, do rr allocation for better
377          * creation performance
378          */
379         qos->lq_same_space = 0;
380         if ((ba_max * (256 - qos->lq_threshold_rr)) >> 8 < ba_min &&
381             (ia_max * (256 - qos->lq_threshold_rr)) >> 8 < ia_min) {
382                 qos->lq_same_space = 1;
383                 /* Reset weights for the next time we enter qos mode */
384                 qos->lq_reset = 1;
385         }
386         rc = 0;
387
388 out:
389         if (!rc && qos->lq_same_space)
390                 RETURN(-EAGAIN);
391
392         RETURN(rc);
393 }
394 EXPORT_SYMBOL(lqos_calc_penalties);
395
396 bool lqos_is_usable(struct lu_qos *qos, __u32 active_tgt_nr)
397 {
398         if (!qos->lq_dirty && qos->lq_same_space)
399                 return false;
400
401         if (active_tgt_nr < 2)
402                 return false;
403
404         return true;
405 }
406 EXPORT_SYMBOL(lqos_is_usable);
407
408 /**
409  * Calculate weight for a given tgt.
410  *
411  * The final tgt weight is bavail >> 16 * iavail >> 8 minus the tgt and server
412  * penalties.  See lqos_calc_ppts() for how penalties are calculated.
413  *
414  * \param[in] tgt       target descriptor
415  */
416 void lqos_calc_weight(struct lu_tgt_desc *tgt)
417 {
418         struct lu_tgt_qos *ltq = &tgt->ltd_qos;
419         __u64 temp, temp2;
420
421         temp = (tgt_statfs_bavail(tgt) >> 16) * (tgt_statfs_iavail(tgt) >> 8);
422         temp2 = ltq->ltq_penalty + ltq->ltq_svr->lsq_penalty;
423         if (temp < temp2)
424                 ltq->ltq_weight = 0;
425         else
426                 ltq->ltq_weight = temp - temp2;
427 }
428 EXPORT_SYMBOL(lqos_calc_weight);
429
430 /**
431  * Re-calculate weights.
432  *
433  * The function is called when some target was used for a new object. In
434  * this case we should re-calculate all the weights to keep new allocations
435  * balanced well.
436  *
437  * \param[in] qos               lu_qos
438  * \param[in] ltd               lu_tgt_descs
439  * \param[in] tgt               target where a new object was placed
440  * \param[in] active_tgt_nr     active tgt number
441  * \param[out] total_wt new total weight for the pool
442  *
443  * \retval              0
444  */
445 int lqos_recalc_weight(struct lu_qos *qos, struct lu_tgt_descs *ltd,
446                        struct lu_tgt_desc *tgt, __u32 active_tgt_nr,
447                        __u64 *total_wt)
448 {
449         struct lu_tgt_qos *ltq;
450         struct lu_svr_qos *svr;
451
452         ENTRY;
453
454         ltq = &tgt->ltd_qos;
455         LASSERT(ltq);
456
457         /* Don't allocate on this device anymore, until the next alloc_qos */
458         ltq->ltq_usable = 0;
459
460         svr = ltq->ltq_svr;
461
462         /*
463          * Decay old penalty by half (we're adding max penalty, and don't
464          * want it to run away.)
465          */
466         ltq->ltq_penalty >>= 1;
467         svr->lsq_penalty >>= 1;
468
469         /* mark the server and tgt as recently used */
470         ltq->ltq_used = svr->lsq_used = ktime_get_real_seconds();
471
472         /* Set max penalties for this tgt and server */
473         ltq->ltq_penalty += ltq->ltq_penalty_per_obj * active_tgt_nr;
474         svr->lsq_penalty += svr->lsq_penalty_per_obj * active_tgt_nr;
475
476         /* Decrease all MDS penalties */
477         list_for_each_entry(svr, &qos->lq_svr_list, lsq_svr_list) {
478                 if (svr->lsq_penalty < svr->lsq_penalty_per_obj)
479                         svr->lsq_penalty = 0;
480                 else
481                         svr->lsq_penalty -= svr->lsq_penalty_per_obj;
482         }
483
484         *total_wt = 0;
485         /* Decrease all tgt penalties */
486         ltd_foreach_tgt(ltd, tgt) {
487                 if (!tgt->ltd_active)
488                         continue;
489
490                 if (ltq->ltq_penalty < ltq->ltq_penalty_per_obj)
491                         ltq->ltq_penalty = 0;
492                 else
493                         ltq->ltq_penalty -= ltq->ltq_penalty_per_obj;
494
495                 lqos_calc_weight(tgt);
496
497                 /* Recalc the total weight of usable osts */
498                 if (ltq->ltq_usable)
499                         *total_wt += ltq->ltq_weight;
500
501                 CDEBUG(D_OTHER, "recalc tgt %d usable=%d avail=%llu"
502                           " tgtppo=%llu tgtp=%llu svrppo=%llu"
503                           " svrp=%llu wt=%llu\n",
504                           tgt->ltd_index, ltq->ltq_usable,
505                           tgt_statfs_bavail(tgt) >> 10,
506                           ltq->ltq_penalty_per_obj >> 10,
507                           ltq->ltq_penalty >> 10,
508                           ltq->ltq_svr->lsq_penalty_per_obj >> 10,
509                           ltq->ltq_svr->lsq_penalty >> 10,
510                           ltq->ltq_weight >> 10);
511         }
512
513         RETURN(0);
514 }
515 EXPORT_SYMBOL(lqos_recalc_weight);