]> git.saurik.com Git - apple/libpthread.git/blob - src/qos.c
libpthread-218.60.3.tar.gz
[apple/libpthread.git] / src / qos.c
1 /*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include "internal.h"
25
26 #include <_simple.h>
27 #include <mach/mach_vm.h>
28 #include <unistd.h>
29 #include <spawn.h>
30 #include <spawn_private.h>
31 #include <sys/spawn_internal.h>
32 #include <sys/ulock.h>
33
34 // TODO: remove me when internal.h can include *_private.h itself
35 #include "workqueue_private.h"
36 #include "qos_private.h"
37
38 static pthread_priority_t _main_qos = QOS_CLASS_UNSPECIFIED;
39
40 #define PTHREAD_OVERRIDE_SIGNATURE (0x6f766572)
41 #define PTHREAD_OVERRIDE_SIG_DEAD (0x7265766f)
42
43 struct pthread_override_s
44 {
45 uint32_t sig;
46 mach_port_t kthread;
47 pthread_t pthread;
48 pthread_priority_t priority;
49 bool malloced;
50 };
51
52 void
53 _pthread_set_main_qos(pthread_priority_t qos)
54 {
55 _main_qos = qos;
56 }
57
58 int
59 pthread_attr_set_qos_class_np(pthread_attr_t *__attr,
60 qos_class_t __qos_class,
61 int __relative_priority)
62 {
63 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
64 return ENOTSUP;
65 }
66
67 if (__relative_priority > 0 || __relative_priority < QOS_MIN_RELATIVE_PRIORITY) {
68 return EINVAL;
69 }
70
71 int ret = EINVAL;
72 if (__attr->sig == _PTHREAD_ATTR_SIG) {
73 if (!__attr->schedset) {
74 __attr->qosclass = _pthread_priority_make_newest(__qos_class, __relative_priority, 0);
75 __attr->qosset = 1;
76 ret = 0;
77 }
78 }
79
80 return ret;
81 }
82
83 int
84 pthread_attr_get_qos_class_np(pthread_attr_t * __restrict __attr,
85 qos_class_t * __restrict __qos_class,
86 int * __restrict __relative_priority)
87 {
88 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
89 return ENOTSUP;
90 }
91
92 int ret = EINVAL;
93 if (__attr->sig == _PTHREAD_ATTR_SIG) {
94 if (__attr->qosset) {
95 qos_class_t qos; int relpri;
96 _pthread_priority_split_newest(__attr->qosclass, qos, relpri);
97
98 if (__qos_class) { *__qos_class = qos; }
99 if (__relative_priority) { *__relative_priority = relpri; }
100 } else {
101 if (__qos_class) { *__qos_class = 0; }
102 if (__relative_priority) { *__relative_priority = 0; }
103 }
104 ret = 0;
105 }
106
107 return ret;
108 }
109
110 int
111 pthread_set_qos_class_self_np(qos_class_t __qos_class,
112 int __relative_priority)
113 {
114 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
115 return ENOTSUP;
116 }
117
118 if (__relative_priority > 0 || __relative_priority < QOS_MIN_RELATIVE_PRIORITY) {
119 return EINVAL;
120 }
121
122 pthread_priority_t priority = _pthread_priority_make_newest(__qos_class, __relative_priority, 0);
123
124 if (__pthread_supported_features & PTHREAD_FEATURE_SETSELF) {
125 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG, priority, 0);
126 } else {
127 /* We set the thread QoS class in the TSD and then call into the kernel to
128 * read the value out of it and set the QoS class.
129 */
130 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, priority);
131
132 mach_port_t kport = pthread_mach_thread_np(pthread_self());
133 int res = __bsdthread_ctl(BSDTHREAD_CTL_SET_QOS, kport, &pthread_self()->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS], 0);
134
135 if (res == -1) {
136 res = errno;
137 }
138
139 return res;
140 }
141 }
142
143 int
144 pthread_set_qos_class_np(pthread_t __pthread,
145 qos_class_t __qos_class,
146 int __relative_priority)
147 {
148 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
149 return ENOTSUP;
150 }
151
152 if (__relative_priority > 0 || __relative_priority < QOS_MIN_RELATIVE_PRIORITY) {
153 return EINVAL;
154 }
155
156 if (__pthread != pthread_self()) {
157 /* The kext now enforces this anyway, if we check here too, it allows us to call
158 * _pthread_set_properties_self later if we can.
159 */
160 return EPERM;
161 }
162
163 pthread_priority_t priority = _pthread_priority_make_newest(__qos_class, __relative_priority, 0);
164
165 if (__pthread_supported_features & PTHREAD_FEATURE_SETSELF) {
166 /* If we have _pthread_set_properties_self, then we can easily set this using that. */
167 return _pthread_set_properties_self(_PTHREAD_SET_SELF_QOS_FLAG, priority, 0);
168 } else {
169 /* We set the thread QoS class in the TSD and then call into the kernel to
170 * read the value out of it and set the QoS class.
171 */
172 if (__pthread == pthread_self()) {
173 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, priority);
174 } else {
175 __pthread->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS] = priority;
176 }
177
178 mach_port_t kport = pthread_mach_thread_np(__pthread);
179 int res = __bsdthread_ctl(BSDTHREAD_CTL_SET_QOS, kport, &__pthread->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS], 0);
180
181 if (res == -1) {
182 res = errno;
183 }
184
185 return res;
186 }
187 }
188
189 int
190 pthread_get_qos_class_np(pthread_t __pthread,
191 qos_class_t * __restrict __qos_class,
192 int * __restrict __relative_priority)
193 {
194 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
195 return ENOTSUP;
196 }
197
198 pthread_priority_t priority;
199
200 if (__pthread == pthread_self()) {
201 priority = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS);
202 } else {
203 priority = __pthread->tsd[_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS];
204 }
205
206 qos_class_t qos; int relpri;
207 _pthread_priority_split_newest(priority, qos, relpri);
208
209 if (__qos_class) { *__qos_class = qos; }
210 if (__relative_priority) { *__relative_priority = relpri; }
211
212 return 0;
213 }
214
215 qos_class_t
216 qos_class_self(void)
217 {
218 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
219 return QOS_CLASS_UNSPECIFIED;
220 }
221
222 pthread_priority_t p = _pthread_getspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS);
223 qos_class_t c = _pthread_priority_get_qos_newest(p);
224
225 return c;
226 }
227
228 qos_class_t
229 qos_class_main(void)
230 {
231 return _pthread_priority_get_qos_newest(_main_qos);
232 }
233
234 pthread_priority_t
235 _pthread_qos_class_encode(qos_class_t qos_class, int relative_priority, unsigned long flags)
236 {
237 if ((__pthread_supported_features & PTHREAD_FEATURE_QOS_MAINTENANCE) == 0) {
238 return _pthread_priority_make_version2(qos_class, relative_priority, flags);
239 } else {
240 return _pthread_priority_make_newest(qos_class, relative_priority, flags);
241 }
242 }
243
244 qos_class_t
245 _pthread_qos_class_decode(pthread_priority_t priority, int *relative_priority, unsigned long *flags)
246 {
247 qos_class_t qos; int relpri;
248
249 if ((__pthread_supported_features & PTHREAD_FEATURE_QOS_MAINTENANCE) == 0) {
250 _pthread_priority_split_version2(priority, qos, relpri);
251 } else {
252 _pthread_priority_split_newest(priority, qos, relpri);
253 }
254
255 if (relative_priority) { *relative_priority = relpri; }
256 if (flags) { *flags = _pthread_priority_get_flags(priority); }
257 return qos;
258 }
259
260 pthread_priority_t
261 _pthread_qos_class_encode_workqueue(int queue_priority, unsigned long flags)
262 {
263 if ((__pthread_supported_features & PTHREAD_FEATURE_QOS_DEFAULT) == 0) {
264 switch (queue_priority) {
265 case WORKQ_HIGH_PRIOQUEUE:
266 return _pthread_priority_make_version1(QOS_CLASS_USER_INTERACTIVE, 0, flags);
267 case WORKQ_DEFAULT_PRIOQUEUE:
268 return _pthread_priority_make_version1(QOS_CLASS_USER_INITIATED, 0, flags);
269 case WORKQ_LOW_PRIOQUEUE:
270 case WORKQ_NON_INTERACTIVE_PRIOQUEUE:
271 return _pthread_priority_make_version1(QOS_CLASS_UTILITY, 0, flags);
272 case WORKQ_BG_PRIOQUEUE:
273 return _pthread_priority_make_version1(QOS_CLASS_BACKGROUND, 0, flags);
274 default:
275 __pthread_abort();
276 }
277 }
278
279 if ((__pthread_supported_features & PTHREAD_FEATURE_QOS_MAINTENANCE) == 0) {
280 switch (queue_priority) {
281 case WORKQ_HIGH_PRIOQUEUE:
282 return _pthread_priority_make_version2(QOS_CLASS_USER_INITIATED, 0, flags);
283 case WORKQ_DEFAULT_PRIOQUEUE:
284 return _pthread_priority_make_version2(QOS_CLASS_DEFAULT, 0, flags);
285 case WORKQ_LOW_PRIOQUEUE:
286 case WORKQ_NON_INTERACTIVE_PRIOQUEUE:
287 return _pthread_priority_make_version2(QOS_CLASS_UTILITY, 0, flags);
288 case WORKQ_BG_PRIOQUEUE:
289 return _pthread_priority_make_version2(QOS_CLASS_BACKGROUND, 0, flags);
290 /* Legacy dispatch does not use QOS_CLASS_MAINTENANCE, so no need to handle it here */
291 default:
292 __pthread_abort();
293 }
294 }
295
296 switch (queue_priority) {
297 case WORKQ_HIGH_PRIOQUEUE:
298 return _pthread_priority_make_newest(QOS_CLASS_USER_INITIATED, 0, flags);
299 case WORKQ_DEFAULT_PRIOQUEUE:
300 return _pthread_priority_make_newest(QOS_CLASS_DEFAULT, 0, flags);
301 case WORKQ_LOW_PRIOQUEUE:
302 case WORKQ_NON_INTERACTIVE_PRIOQUEUE:
303 return _pthread_priority_make_newest(QOS_CLASS_UTILITY, 0, flags);
304 case WORKQ_BG_PRIOQUEUE:
305 return _pthread_priority_make_newest(QOS_CLASS_BACKGROUND, 0, flags);
306 /* Legacy dispatch does not use QOS_CLASS_MAINTENANCE, so no need to handle it here */
307 default:
308 __pthread_abort();
309 }
310 }
311
312 int
313 _pthread_set_properties_self(_pthread_set_flags_t flags, pthread_priority_t priority, mach_port_t voucher)
314 {
315 if (!(__pthread_supported_features & PTHREAD_FEATURE_SETSELF)) {
316 return ENOTSUP;
317 }
318
319 int rv = __bsdthread_ctl(BSDTHREAD_CTL_SET_SELF, priority, voucher, flags);
320
321 /* Set QoS TSD if we succeeded or only failed the voucher half. */
322 if ((flags & _PTHREAD_SET_SELF_QOS_FLAG) != 0) {
323 if (rv == 0 || errno == ENOENT) {
324 _pthread_setspecific_direct(_PTHREAD_TSD_SLOT_PTHREAD_QOS_CLASS, priority);
325 }
326 }
327
328 if (rv) {
329 rv = errno;
330 }
331 return rv;
332 }
333
334 int
335 pthread_set_fixedpriority_self(void)
336 {
337 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
338 return ENOTSUP;
339 }
340
341 if (__pthread_supported_features & PTHREAD_FEATURE_SETSELF) {
342 return _pthread_set_properties_self(_PTHREAD_SET_SELF_FIXEDPRIORITY_FLAG, 0, 0);
343 } else {
344 return ENOTSUP;
345 }
346 }
347
348 int
349 pthread_set_timeshare_self(void)
350 {
351 if (!(__pthread_supported_features & PTHREAD_FEATURE_BSDTHREADCTL)) {
352 return ENOTSUP;
353 }
354
355 if (__pthread_supported_features & PTHREAD_FEATURE_SETSELF) {
356 return _pthread_set_properties_self(_PTHREAD_SET_SELF_TIMESHARE_FLAG, 0, 0);
357 } else {
358 return ENOTSUP;
359 }
360 }
361
362
363 pthread_override_t
364 pthread_override_qos_class_start_np(pthread_t __pthread, qos_class_t __qos_class, int __relative_priority)
365 {
366 pthread_override_t rv;
367 kern_return_t kr;
368 int res = 0;
369
370 /* For now, we don't have access to malloc. So we'll have to vm_allocate this, which means the tiny struct is going
371 * to use an entire page.
372 */
373 bool did_malloc = true;
374
375 mach_vm_address_t vm_addr = malloc(sizeof(struct pthread_override_s));
376 if (!vm_addr) {
377 vm_addr = vm_page_size;
378 did_malloc = false;
379
380 kr = mach_vm_allocate(mach_task_self(), &vm_addr, round_page(sizeof(struct pthread_override_s)), VM_MAKE_TAG(VM_MEMORY_LIBDISPATCH) | VM_FLAGS_ANYWHERE);
381 if (kr != KERN_SUCCESS) {
382 errno = ENOMEM;
383 return (_Nonnull pthread_override_t) NULL;
384 }
385 }
386
387 rv = (pthread_override_t)vm_addr;
388 rv->sig = PTHREAD_OVERRIDE_SIGNATURE;
389 rv->pthread = __pthread;
390 rv->kthread = pthread_mach_thread_np(__pthread);
391 rv->priority = _pthread_priority_make_newest(__qos_class, __relative_priority, 0);
392 rv->malloced = did_malloc;
393
394 /* To ensure that the kernel port that we keep stays valid, we retain it here. */
395 kr = mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, 1);
396 if (kr != KERN_SUCCESS) {
397 res = EINVAL;
398 }
399
400 if (res == 0) {
401 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, rv->kthread, rv->priority, (uintptr_t)rv);
402
403 if (res != 0) {
404 mach_port_mod_refs(mach_task_self(), rv->kthread, MACH_PORT_RIGHT_SEND, -1);
405 }
406 }
407
408 if (res != 0) {
409 if (did_malloc) {
410 free(rv);
411 } else {
412 mach_vm_deallocate(mach_task_self(), vm_addr, round_page(sizeof(struct pthread_override_s)));
413 }
414 rv = NULL;
415 }
416 return (_Nonnull pthread_override_t) rv;
417 }
418
419 int
420 pthread_override_qos_class_end_np(pthread_override_t override)
421 {
422 kern_return_t kr;
423 int res = 0;
424
425 /* Double-free is a fault. Swap the signature and check the old one. */
426 if (__sync_swap(&override->sig, PTHREAD_OVERRIDE_SIG_DEAD) != PTHREAD_OVERRIDE_SIGNATURE) {
427 __builtin_trap();
428 }
429
430 override->sig = PTHREAD_OVERRIDE_SIG_DEAD;
431
432 /* Always consumes (and deallocates) the pthread_override_t object given. */
433 res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, override->kthread, (uintptr_t)override, 0);
434 if (res == -1) { res = errno; }
435
436 /* EFAULT from the syscall means we underflowed. Crash here. */
437 if (res == EFAULT) {
438 // <rdar://problem/17645082> Disable the trap-on-underflow, it doesn't co-exist
439 // with dispatch resetting override counts on threads.
440 //__builtin_trap();
441 res = 0;
442 }
443
444 kr = mach_port_mod_refs(mach_task_self(), override->kthread, MACH_PORT_RIGHT_SEND, -1);
445 if (kr != KERN_SUCCESS) {
446 res = EINVAL;
447 }
448
449 if (override->malloced) {
450 free(override);
451 } else {
452 kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)override, round_page(sizeof(struct pthread_override_s)));
453 if (kr != KERN_SUCCESS) {
454 res = EINVAL;
455 }
456 }
457
458 return res;
459 }
460
461 int
462 _pthread_qos_override_start_direct(mach_port_t thread, pthread_priority_t priority, void *resource)
463 {
464 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_START, thread, priority, (uintptr_t)resource);
465 if (res == -1) { res = errno; }
466 return res;
467 }
468
469 int
470 _pthread_qos_override_end_direct(mach_port_t thread, void *resource)
471 {
472 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_END, thread, (uintptr_t)resource, 0);
473 if (res == -1) { res = errno; }
474 return res;
475 }
476
477 int
478 _pthread_override_qos_class_start_direct(mach_port_t thread, pthread_priority_t priority)
479 {
480 // use pthread_self as the default per-thread memory allocation to track the override in the kernel
481 return _pthread_qos_override_start_direct(thread, priority, pthread_self());
482 }
483
484 int
485 _pthread_override_qos_class_end_direct(mach_port_t thread)
486 {
487 // use pthread_self as the default per-thread memory allocation to track the override in the kernel
488 return _pthread_qos_override_end_direct(thread, pthread_self());
489 }
490
491 int
492 _pthread_workqueue_override_start_direct(mach_port_t thread, pthread_priority_t priority)
493 {
494 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, 0);
495 if (res == -1) { res = errno; }
496 return res;
497 }
498
499 int
500 _pthread_workqueue_override_start_direct_check_owner(mach_port_t thread, pthread_priority_t priority, mach_port_t *ulock_addr)
501 {
502 #if !TARGET_OS_IPHONE
503 static boolean_t kernel_supports_owner_check = TRUE;
504 if (!kernel_supports_owner_check) {
505 ulock_addr = NULL;
506 }
507 #endif
508
509 for (;;) {
510 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH, thread, priority, ulock_addr);
511 if (res == -1) { res = errno; }
512 #if !TARGET_OS_IPHONE
513 if (ulock_addr && res == EINVAL) {
514 if ((uintptr_t)ulock_addr % _Alignof(_Atomic uint32_t)) {
515 // do not mute bad ulock addresses related errors
516 return EINVAL;
517 }
518 // backward compatibility for the XBS chroot
519 // BSDTHREAD_CTL_QOS_OVERRIDE_DISPATCH used to return EINVAL if
520 // arg3 was non NULL.
521 kernel_supports_owner_check = FALSE;
522 ulock_addr = NULL;
523 continue;
524 }
525 #endif
526 if (ulock_addr && res == EFAULT) {
527 // kernel wants us to redrive the call, so while we refault the
528 // memory, also revalidate the owner
529 uint32_t uval = os_atomic_load(ulock_addr, relaxed);
530 if (ulock_owner_value_to_port_name(uval) != thread) {
531 return ESTALE;
532 }
533 continue;
534 }
535
536 return res;
537 }
538 }
539
540 int
541 _pthread_workqueue_override_reset(void)
542 {
543 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_OVERRIDE_RESET, 0, 0, 0);
544 if (res == -1) { res = errno; }
545 return res;
546 }
547
548 int
549 _pthread_workqueue_asynchronous_override_add(mach_port_t thread, pthread_priority_t priority, void *resource)
550 {
551 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_ADD, thread, priority, (uintptr_t)resource);
552 if (res == -1) { res = errno; }
553 return res;
554 }
555
556 int
557 _pthread_workqueue_asynchronous_override_reset_self(void *resource)
558 {
559 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET,
560 0 /* !reset_all */,
561 (uintptr_t)resource,
562 0);
563 if (res == -1) { res = errno; }
564 return res;
565 }
566
567 int
568 _pthread_workqueue_asynchronous_override_reset_all_self(void)
569 {
570 int res = __bsdthread_ctl(BSDTHREAD_CTL_QOS_DISPATCH_ASYNCHRONOUS_OVERRIDE_RESET,
571 1 /* reset_all */,
572 0,
573 0);
574 if (res == -1) { res = errno; }
575 return res;
576 }
577
578 int
579 posix_spawnattr_set_qos_class_np(posix_spawnattr_t * __restrict __attr, qos_class_t __qos_class)
580 {
581 switch (__qos_class) {
582 case QOS_CLASS_UTILITY:
583 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_UTILITY);
584 case QOS_CLASS_BACKGROUND:
585 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_BACKGROUND);
586 case QOS_CLASS_MAINTENANCE:
587 return posix_spawnattr_set_qos_clamp_np(__attr, POSIX_SPAWN_PROC_CLAMP_MAINTENANCE);
588 default:
589 return EINVAL;
590 }
591 }
592
593 int
594 posix_spawnattr_get_qos_class_np(const posix_spawnattr_t *__restrict __attr, qos_class_t * __restrict __qos_class)
595 {
596 uint64_t clamp;
597
598 if (!__qos_class) {
599 return EINVAL;
600 }
601
602 int rv = posix_spawnattr_get_qos_clamp_np(__attr, &clamp);
603 if (rv != 0) {
604 return rv;
605 }
606
607 switch (clamp) {
608 case POSIX_SPAWN_PROC_CLAMP_UTILITY:
609 *__qos_class = QOS_CLASS_UTILITY;
610 break;
611 case POSIX_SPAWN_PROC_CLAMP_BACKGROUND:
612 *__qos_class = QOS_CLASS_BACKGROUND;
613 break;
614 case POSIX_SPAWN_PROC_CLAMP_MAINTENANCE:
615 *__qos_class = QOS_CLASS_MAINTENANCE;
616 break;
617 default:
618 *__qos_class = QOS_CLASS_UNSPECIFIED;
619 break;
620 }
621
622 return 0;
623 }