VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/BandwidthControlImpl.cpp

Last change on this file was 98288, checked in by vboxsync, 16 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/* $Id: BandwidthControlImpl.cpp 98288 2023-01-24 15:32:43Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#define LOG_GROUP LOG_GROUP_MAIN_BANDWIDTHCONTROL
29#include "BandwidthControlImpl.h"
30#include "BandwidthGroupImpl.h"
31#include "MachineImpl.h"
32#include "Global.h"
33
34#include "AutoStateDep.h"
35#include "AutoCaller.h"
36#include "LoggingNew.h"
37
38#include <iprt/cpp/utils.h>
39#include <VBox/com/array.h>
40#include <VBox/param.h>
41#include <algorithm>
42
43// defines
44/////////////////////////////////////////////////////////////////////////////
45
46// constructor / destructor
47/////////////////////////////////////////////////////////////////////////////
48DEFINE_EMPTY_CTOR_DTOR(BandwidthControl)
49
50
51HRESULT BandwidthControl::FinalConstruct()
52{
53 return BaseFinalConstruct();
54}
55
56void BandwidthControl::FinalRelease()
57{
58 uninit();
59 BaseFinalRelease();
60}
61
62// public initializer/uninitializer for internal purposes only
63/////////////////////////////////////////////////////////////////////////////
64
65/**
66 * Initializes the bandwidth group object.
67 *
68 * @returns COM result indicator.
69 * @param aParent Pointer to our parent object.
70 */
71HRESULT BandwidthControl::init(Machine *aParent)
72{
73 LogFlowThisFunc(("aParent=%p\n", aParent));
74
75 ComAssertRet(aParent, E_INVALIDARG);
76
77 /* Enclose the state transition NotReady->InInit->Ready */
78 AutoInitSpan autoInitSpan(this);
79 AssertReturn(autoInitSpan.isOk(), E_FAIL);
80
81 m = new Data(aParent);
82
83 /* m->pPeer is left null */
84
85 m->llBandwidthGroups.allocate();
86
87 /* Confirm a successful initialization */
88 autoInitSpan.setSucceeded();
89
90 return S_OK;
91}
92
93/**
94 * Initializes the object given another object
95 * (a kind of copy constructor). This object shares data with
96 * the object passed as an argument.
97 *
98 * @note This object must be destroyed before the original object
99 * it shares data with is destroyed.
100 *
101 * @note Locks @a aThat object for writing if @a aReshare is @c true, or for
102 * reading if @a aReshare is false.
103 */
104HRESULT BandwidthControl::init(Machine *aParent,
105 BandwidthControl *aThat)
106{
107 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
108
109 ComAssertRet(aParent && aThat, E_INVALIDARG);
110
111 /* Enclose the state transition NotReady->InInit->Ready */
112 AutoInitSpan autoInitSpan(this);
113 AssertReturn(autoInitSpan.isOk(), E_FAIL);
114
115 m = new Data(aParent);
116
117 /* sanity */
118 AutoCaller thatCaller(aThat);
119 AssertComRCReturnRC(thatCaller.hrc());
120
121 unconst(m->pPeer) = aThat;
122 AutoWriteLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
123
124 /* create copies of all groups */
125 m->llBandwidthGroups.allocate();
126 BandwidthGroupList::const_iterator it;
127 for (it = aThat->m->llBandwidthGroups->begin();
128 it != aThat->m->llBandwidthGroups->end();
129 ++it)
130 {
131 ComObjPtr<BandwidthGroup> group;
132 group.createObject();
133 group->init(this, *it);
134 m->llBandwidthGroups->push_back(group);
135 }
136
137 /* Confirm successful initialization */
138 autoInitSpan.setSucceeded();
139
140 return S_OK;
141}
142
143/**
144 * Initializes the bandwidth control object given another guest object
145 * (a kind of copy constructor). This object makes a private copy of data
146 * of the original object passed as an argument.
147 */
148HRESULT BandwidthControl::initCopy(Machine *aParent, BandwidthControl *aThat)
149{
150 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
151
152 ComAssertRet(aParent && aThat, E_INVALIDARG);
153
154 /* Enclose the state transition NotReady->InInit->Ready */
155 AutoInitSpan autoInitSpan(this);
156 AssertReturn(autoInitSpan.isOk(), E_FAIL);
157
158 m = new Data(aParent);
159 /* m->pPeer is left null */
160
161 AutoCaller thatCaller(aThat);
162 AssertComRCReturnRC(thatCaller.hrc());
163
164 AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
165
166 /* create copies of all groups */
167 m->llBandwidthGroups.allocate();
168 BandwidthGroupList::const_iterator it;
169 for (it = aThat->m->llBandwidthGroups->begin();
170 it != aThat->m->llBandwidthGroups->end();
171 ++it)
172 {
173 ComObjPtr<BandwidthGroup> group;
174 group.createObject();
175 group->initCopy(this, *it);
176 m->llBandwidthGroups->push_back(group);
177 }
178
179 /* Confirm a successful initialization */
180 autoInitSpan.setSucceeded();
181
182 return S_OK;
183}
184
185
186/**
187 * @note Locks this object for writing, together with the peer object
188 * represented by @a aThat (locked for reading).
189 */
190void BandwidthControl::i_copyFrom(BandwidthControl *aThat)
191{
192 AssertReturnVoid(aThat != NULL);
193
194 /* sanity */
195 AutoCaller autoCaller(this);
196 AssertComRCReturnVoid(autoCaller.hrc());
197
198 /* sanity too */
199 AutoCaller thatCaller(aThat);
200 AssertComRCReturnVoid(thatCaller.hrc());
201
202 /* even more sanity */
203 AutoAnyStateDependency adep(m->pParent);
204 AssertComRCReturnVoid(adep.hrc());
205 /* Machine::copyFrom() may not be called when the VM is running */
206 AssertReturnVoid(!Global::IsOnline(adep.machineState()));
207
208 /* peer is not modified, lock it for reading (aThat is "master" so locked
209 * first) */
210 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
211 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
212
213 /* create private copies of all bandwidth groups */
214 m->llBandwidthGroups.backup();
215 m->llBandwidthGroups->clear();
216 BandwidthGroupList::const_iterator it;
217 for (it = aThat->m->llBandwidthGroups->begin();
218 it != aThat->m->llBandwidthGroups->end();
219 ++it)
220 {
221 ComObjPtr<BandwidthGroup> group;
222 group.createObject();
223 group->initCopy(this, *it);
224 m->llBandwidthGroups->push_back(group);
225 }
226}
227
228/** @note Locks objects for writing! */
229void BandwidthControl::i_rollback()
230{
231 AutoCaller autoCaller(this);
232 AssertComRCReturnVoid(autoCaller.hrc());
233
234 /* we need the machine state */
235 AutoAnyStateDependency adep(m->pParent);
236 AssertComRCReturnVoid(adep.hrc());
237
238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
239 BandwidthGroupList::const_iterator it;
240
241 if (!m->llBandwidthGroups.isNull())
242 {
243 if (m->llBandwidthGroups.isBackedUp())
244 {
245 /* unitialize all new groups (absent in the backed up list). */
246 BandwidthGroupList *backedList = m->llBandwidthGroups.backedUpData();
247 for (it = m->llBandwidthGroups->begin();
248 it != m->llBandwidthGroups->end();
249 ++it)
250 {
251 if ( std::find(backedList->begin(), backedList->end(), *it)
252 == backedList->end())
253 (*it)->uninit();
254 }
255
256 /* restore the list */
257 m->llBandwidthGroups.rollback();
258 }
259
260 /* rollback any changes to groups after restoring the list */
261 for (it = m->llBandwidthGroups->begin();
262 it != m->llBandwidthGroups->end();
263 ++it)
264 (*it)->i_rollback();
265 }
266}
267
268void BandwidthControl::i_commit()
269{
270 bool commitBandwidthGroups = false;
271 BandwidthGroupList::const_iterator it;
272
273 if (m->llBandwidthGroups.isBackedUp())
274 {
275 m->llBandwidthGroups.commit();
276
277 if (m->pPeer)
278 {
279 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
280
281 /* Commit all changes to new groups (this will reshare data with
282 * peers for those who have peers) */
283 BandwidthGroupList *newList = new BandwidthGroupList();
284 for (it = m->llBandwidthGroups->begin();
285 it != m->llBandwidthGroups->end();
286 ++it)
287 {
288 (*it)->i_commit();
289
290 /* look if this group has a peer group */
291 ComObjPtr<BandwidthGroup> peer = (*it)->i_getPeer();
292 if (!peer)
293 {
294 /* no peer means the device is a newly created one;
295 * create a peer owning data this device share it with */
296 peer.createObject();
297 peer->init(m->pPeer, *it, true /* aReshare */);
298 }
299 else
300 {
301 /* remove peer from the old list */
302 m->pPeer->m->llBandwidthGroups->remove(peer);
303 }
304 /* and add it to the new list */
305 newList->push_back(peer);
306 }
307
308 /* uninit old peer's groups that are left */
309 for (it = m->pPeer->m->llBandwidthGroups->begin();
310 it != m->pPeer->m->llBandwidthGroups->end();
311 ++it)
312 (*it)->uninit();
313
314 /* attach new list of groups to our peer */
315 m->pPeer->m->llBandwidthGroups.attach(newList);
316 }
317 else
318 {
319 /* we have no peer (our parent is the newly created machine);
320 * just commit changes to devices */
321 commitBandwidthGroups = true;
322 }
323 }
324 else
325 {
326 /* the list of groups itself is not changed,
327 * just commit changes to groups themselves */
328 commitBandwidthGroups = true;
329 }
330
331 if (commitBandwidthGroups)
332 {
333 for (it = m->llBandwidthGroups->begin();
334 it != m->llBandwidthGroups->end();
335 ++it)
336 (*it)->i_commit();
337 }
338}
339
340/**
341 * Uninitializes the instance and sets the ready flag to FALSE.
342 * Called either from FinalRelease() or by the parent when it gets destroyed.
343 */
344void BandwidthControl::uninit()
345{
346 LogFlowThisFunc(("\n"));
347
348 /* Enclose the state transition Ready->InUninit->NotReady */
349 AutoUninitSpan autoUninitSpan(this);
350 if (autoUninitSpan.uninitDone())
351 return;
352
353 // uninit all groups on the list (it's a standard std::list not an ObjectsList
354 // so we must uninit() manually)
355 BandwidthGroupList::iterator it;
356 for (it = m->llBandwidthGroups->begin();
357 it != m->llBandwidthGroups->end();
358 ++it)
359 (*it)->uninit();
360
361 m->llBandwidthGroups.free();
362
363 unconst(m->pPeer) = NULL;
364 unconst(m->pParent) = NULL;
365
366 delete m;
367 m = NULL;
368}
369
370/**
371 * Returns a bandwidth group object with the given name.
372 *
373 * @param aName bandwidth group name to find
374 * @param aBandwidthGroup where to return the found bandwidth group
375 * @param aSetError true to set extended error info on failure
376 */
377HRESULT BandwidthControl::i_getBandwidthGroupByName(const com::Utf8Str &aName,
378 ComObjPtr<BandwidthGroup> &aBandwidthGroup,
379 bool aSetError /* = false */)
380{
381 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
382
383 for (BandwidthGroupList::const_iterator it = m->llBandwidthGroups->begin();
384 it != m->llBandwidthGroups->end();
385 ++it)
386 {
387 if ((*it)->i_getName() == aName)
388 {
389 aBandwidthGroup = (*it);
390 return S_OK;
391 }
392 }
393
394 if (aSetError)
395 return setError(VBOX_E_OBJECT_NOT_FOUND,
396 tr("Could not find a bandwidth group named '%s'"),
397 aName.c_str());
398 return VBOX_E_OBJECT_NOT_FOUND;
399}
400// To do
401HRESULT BandwidthControl::createBandwidthGroup(const com::Utf8Str &aName,
402 BandwidthGroupType_T aType,
403 LONG64 aMaxBytesPerSec)
404{
405 /*
406 * Validate input.
407 */
408 if (aMaxBytesPerSec < 0)
409 return setError(E_INVALIDARG, tr("Bandwidth group limit cannot be negative"));
410 switch (aType)
411 {
412 case BandwidthGroupType_Null: /*??*/
413 case BandwidthGroupType_Disk:
414 break;
415 case BandwidthGroupType_Network:
416 if (aName.length() > PDM_NET_SHAPER_MAX_NAME_LEN)
417 return setError(E_INVALIDARG, tr("Bandwidth name is too long: %zu, max %u"),
418 aName.length(), PDM_NET_SHAPER_MAX_NAME_LEN);
419 break;
420 default:
421 AssertFailedReturn(setError(E_INVALIDARG, tr("Invalid group type: %d"), aType));
422 }
423 if (aName.isEmpty())
424 return setError(E_INVALIDARG, tr("Bandwidth group name must not be empty")); /* ConsoleImpl2.cpp fails then */
425
426 /*
427 * The machine needs to be mutable:
428 */
429 AutoMutableOrSavedStateDependency adep(m->pParent);
430 HRESULT hrc = adep.hrc();
431 if (SUCCEEDED(hrc))
432 {
433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
434
435 /*
436 * Check that the group doesn't already exist:
437 */
438 ComObjPtr<BandwidthGroup> group;
439 hrc = i_getBandwidthGroupByName(aName, group, false /* aSetError */);
440 if (FAILED(hrc))
441 {
442 /*
443 * There is an upper limit of the number of network groups imposed by PDM.
444 */
445 size_t cNetworkGroups = 0;
446 if (aType == BandwidthGroupType_Network)
447 for (BandwidthGroupList::const_iterator it = m->llBandwidthGroups->begin();
448 it != m->llBandwidthGroups->end();
449 ++it)
450 if ((*it)->i_getType() == BandwidthGroupType_Network)
451 cNetworkGroups++;
452 if (cNetworkGroups < PDM_NET_SHAPER_MAX_GROUPS)
453 {
454 /*
455 * Create the new group.
456 */
457 hrc = group.createObject();
458 if (SUCCEEDED(hrc))
459 {
460 hrc = group->init(this, aName, aType, aMaxBytesPerSec);
461 if (SUCCEEDED(hrc))
462 {
463 /*
464 * Add it to the settings.
465 */
466 m->pParent->i_setModified(Machine::IsModified_BandwidthControl);
467 m->llBandwidthGroups.backup();
468 m->llBandwidthGroups->push_back(group);
469 hrc = S_OK;
470 }
471 }
472 }
473 else
474 hrc = setError(E_FAIL, tr("Too many network bandwidth groups (max %u)"), PDM_NET_SHAPER_MAX_GROUPS);
475 }
476 else
477 hrc = setError(VBOX_E_OBJECT_IN_USE, tr("Bandwidth group named '%s' already exists"), aName.c_str());
478 }
479 return hrc;
480}
481
482HRESULT BandwidthControl::deleteBandwidthGroup(const com::Utf8Str &aName)
483{
484 /* the machine needs to be mutable */
485 AutoMutableOrSavedStateDependency adep(m->pParent);
486 if (FAILED(adep.hrc())) return adep.hrc();
487
488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
489
490 ComObjPtr<BandwidthGroup> group;
491 HRESULT hrc = i_getBandwidthGroupByName(aName, group, true /* aSetError */);
492 if (FAILED(hrc)) return hrc;
493
494 if (group->i_getReferences() != 0)
495 return setError(VBOX_E_OBJECT_IN_USE,
496 tr("The bandwidth group '%s' is still in use"), aName.c_str());
497
498 /* We can remove it now. */
499 m->pParent->i_setModified(Machine::IsModified_BandwidthControl);
500 m->llBandwidthGroups.backup();
501
502 group->i_unshare();
503
504 m->llBandwidthGroups->remove(group);
505
506 /* inform the direct session if any */
507 alock.release();
508 //onStorageControllerChange(); @todo
509
510 return S_OK;
511}
512
513HRESULT BandwidthControl::getNumGroups(ULONG *aNumGroups)
514{
515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
516
517 *aNumGroups = (ULONG)m->llBandwidthGroups->size();
518
519 return S_OK;
520}
521
522HRESULT BandwidthControl::getBandwidthGroup(const com::Utf8Str &aName, ComPtr<IBandwidthGroup> &aBandwidthGroup)
523{
524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
525
526 ComObjPtr<BandwidthGroup> group;
527 HRESULT hrc = i_getBandwidthGroupByName(aName, group, true /* aSetError */);
528 if (SUCCEEDED(hrc))
529 group.queryInterfaceTo(aBandwidthGroup.asOutParam());
530
531 return hrc;
532}
533
534HRESULT BandwidthControl::getAllBandwidthGroups(std::vector<ComPtr<IBandwidthGroup> > &aBandwidthGroups)
535{
536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
537 aBandwidthGroups.resize(0);
538 BandwidthGroupList::const_iterator it;
539 for (it = m->llBandwidthGroups->begin();
540 it != m->llBandwidthGroups->end();
541 ++it)
542 aBandwidthGroups.push_back(*it);
543
544 return S_OK;
545}
546
547HRESULT BandwidthControl::i_loadSettings(const settings::IOSettings &data)
548{
549 HRESULT hrc = S_OK;
550
551 AutoCaller autoCaller(this);
552 AssertComRCReturnRC(autoCaller.hrc());
553 settings::BandwidthGroupList::const_iterator it;
554 for (it = data.llBandwidthGroups.begin();
555 it != data.llBandwidthGroups.end();
556 ++it)
557 {
558 const settings::BandwidthGroup &gr = *it;
559 hrc = createBandwidthGroup(gr.strName, gr.enmType, (LONG64)gr.cMaxBytesPerSec);
560 if (FAILED(hrc)) break;
561 }
562
563 return hrc;
564}
565
566HRESULT BandwidthControl::i_saveSettings(settings::IOSettings &data)
567{
568 AutoCaller autoCaller(this);
569 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
570
571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
572 data.llBandwidthGroups.clear();
573 BandwidthGroupList::const_iterator it;
574 for (it = m->llBandwidthGroups->begin();
575 it != m->llBandwidthGroups->end();
576 ++it)
577 {
578 AutoWriteLock groupLock(*it COMMA_LOCKVAL_SRC_POS);
579 settings::BandwidthGroup group;
580
581 group.strName = (*it)->i_getName();
582 group.enmType = (*it)->i_getType();
583 group.cMaxBytesPerSec = (uint64_t)(*it)->i_getMaxBytesPerSec();
584
585 data.llBandwidthGroups.push_back(group);
586 }
587
588 return S_OK;
589}
590
591Machine * BandwidthControl::i_getMachine() const
592{
593 return m->pParent;
594}
595
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use