VirtualBox

source: vbox/trunk/src/VBox/Main/BandwidthControlImpl.cpp@ 34587

Last change on this file since 34587 was 34587, checked in by vboxsync, 14 years ago

Main: Bandwidth groups for disks (and later network)

This introduces two new interfaces. The first one named IBandwidthGroup
represents one I/O limit and can be assigned to several mediums which
share this limit (which works only for harddisk images with the disabled
host cache).
The second one IBandwdithControl manages the groups and can create new ones
and destroy them if not required anymore.

VBoxManage: commands to access the bandwidth groups

Syntax:
VBoxManage storageattach <uuid|vmname>

...
--bandwidthgroup <name>

--bandwidthgroup assigns the specified device to the given group.

VBoxManage bandwidthctl <uuid|vmname>

--name <name>
--add disk|network
--limit <megabytes per second>
--delete

The --name parameter gives the name of the bandwidth group.
--add creates a new group of the given type (only disk is implemented so far)

with the given name.

--limit sets the limit to the given amount of MB/s

Note that limit can be changed while the VM is running. The VM
will immediately pick up the new limit for the given group name.

--delete deletes the group with the given name if it isn't used anymore.

Trying to delete a still used group will result in an error.

Example:

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 20
Creates a group named Test having a 20 MB/s limit.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup Limit
Adds a new disk to the SATA controller and assigns the bandwidth group Limit to it.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup none
Removes the bandwidth limit from the disk.

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 10
Changes the limit of bandwidth group Limit to 10 MB/s. If the VM is running the limit will be picked up
immediately.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette