aboutsummaryrefslogblamecommitdiffstats
path: root/litmus/dgl.c
blob: 6c1267839123ca9195b9c775305e78a7cca98785 (plain) (tree)
1
2
3
4
5
6
7
                        

                       



                               





                                                                   



                                                                

                                                                         




                                                                                
                                                                              
















                                                                         



                                                               
                                 





                                                                      

                                                           



                                      








                                                                            

                                        
                                             


                                                   
                                                            






                          






                                                                    

              











                                                                               
                           
                                               




                                       






                                                   


                                                       

                                                         



                            
                                             



                                               

                                                                      
                                        
                         
















                                                                                


                                   












                                                                   
                                                            




                                                      




                                                                      


                                             
                                                        
































                                                                        
                                                          


                                                
                                                          










































                                                                               
                                                          











                                                                 
                                                                            








                                                                               
                                                                  











                                                                       
#include <linux/sched.h>
#include <linux/slab.h>

#include <litmus/litmus.h>
#include <litmus/dgl.h>
#include <litmus/sched_trace.h>

#define MASK_SIZE     (sizeof(unsigned long) * 8)

/* Return number of MASK_SIZE fields needed to store a mask in d */
#define WP(num, word) (num / word + (num % word != 0))
#define MASK_WORDS(d) WP(d->num_resources, MASK_SIZE)

/* Word, bit -> resource id */
#define ri(w, b) (w * MASK_SIZE + b)

 /* For loop, where @i iterates over each set bit in @bit_arr */
#define for_each_resource(bit_arr, d, w, b, i)				\
	for(w = 0; w < MASK_WORDS(d); ++w)				\
		 for(b = find_first_bit(&bit_arr[w],MASK_SIZE), i = ri(w, b);  \
		     b < MASK_SIZE;					       \
		     b = find_next_bit(&bit_arr[w],MASK_SIZE,b+1), i = ri(w, b))

/* Return resource id in dgl @d for resource @r */
#define resource_id(d, r) ((((void*)r) - (void*)((d)->resources))/ sizeof(*r))

/* Return request group of req @r for resource @i */
#define req_group(r, i) (container_of(((void*)r) - sizeof(*r)*(i),	\
				      struct dgl_group_req, requests))

/* Resource id -> word, bit */
static inline void mask_idx(int resource, int *word, int *bit)
{
	*word = resource / MASK_SIZE;
	*bit  = resource % MASK_SIZE;
}


static void print_waiting(struct dgl *dgl, struct dgl_resource *resource)
{
	struct dgl_req *pos;
	struct dgl_group_req *greq;
	unsigned long long last = 0;

	TRACE("List for rid %d\n", resource_id(dgl, resource));
	list_for_each_entry(pos, &resource->waiting, list) {
		greq = pos->greq;
		TRACE("  0x%p with timestamp %llu\n", greq, greq->ts);
		BUG_ON(greq->ts < last);
		last = greq->ts;
	}
}

void dgl_init(struct dgl *dgl, unsigned long num_resources,
	      unsigned long num_replicas)
{
	int i;
	struct dgl_resource *resource;

	dgl->num_replicas  = num_replicas;
	dgl->num_resources = num_resources;

	dgl->resources = kmalloc(sizeof(*dgl->resources) * num_resources,
				 GFP_ATOMIC);
	dgl->acquired  = kmalloc(sizeof(*dgl->acquired) * num_online_cpus(),
				 GFP_ATOMIC);

	for (i = 0; i < num_online_cpus(); ++i)
		dgl->acquired[i] = NULL;

	for (i = 0; i < num_resources; i++) {
		resource = &dgl->resources[i];

		INIT_LIST_HEAD(&resource->waiting);
		resource->free_replicas = dgl->num_replicas;
	}

	dgl->requests = 0;
	dgl->running  = 0;
	dgl->ts = 0;
}

void dgl_free(struct dgl *dgl)
{
	kfree(dgl->resources);
	kfree(dgl->acquired);
}

void dgl_group_req_init(struct dgl *dgl, struct dgl_group_req *greq)
{
	int i;

	greq->requested = kmalloc(sizeof(*greq->requested) * MASK_WORDS(dgl),
				  GFP_ATOMIC);
	greq->waiting   = kmalloc(sizeof(*greq->waiting) * MASK_WORDS(dgl),
				  GFP_ATOMIC);
	greq->requests  = kmalloc(sizeof(*greq->requests) * dgl->num_resources,
				  GFP_ATOMIC);

	BUG_ON(!greq->requested);
	BUG_ON(!greq->waiting);
	BUG_ON(!greq->requests);

	greq->cpu = NO_CPU;
	for (i = 0; i < MASK_WORDS(dgl); ++i) {
		greq->requested[i] = 0;
		greq->waiting[i]   = 0;
	}
}

void dgl_group_req_free(struct dgl_group_req *greq)
{
	kfree(greq->requested);
	kfree(greq->waiting);
	kfree(greq->requests);
}

/**
 * set_req - create request for @replicas of @resource.
 */
void set_req(struct dgl *dgl, struct dgl_group_req *greq,
	     int resource, int replicas)
{
	int word, bit;
	struct dgl_req *req;

	BUG_ON(replicas > dgl->num_replicas);

	mask_idx(resource, &word, &bit);
	__set_bit(bit, &greq->requested[word]);

	TRACE("0x%p requesting %d of %d\n", greq, replicas, resource);

	req = &greq->requests[resource];
	req->greq = greq;
	INIT_LIST_HEAD(&req->list);
	req->replicas = replicas;
}

/*
 * Attempt to fulfill request @req for @resource.
 * Return 1 if successful. If the matching group request has acquired all of
 * its needed resources, this will then set that req as dgl->acquired[cpu].
 */
static unsigned long try_acquire(struct dgl *dgl, struct dgl_resource *resource,
				 struct dgl_req *req)
{
	int word, bit, rid, head, empty, room;
	unsigned long waiting;
	struct dgl_group_req *greq;

	rid  = resource_id(dgl, resource);
	greq = req->greq;

	TRACE("0x%p greq\n", greq);

	head  = resource->waiting.next == &req->list;
	empty = list_empty(&resource->waiting);
	room  = resource->free_replicas >= req->replicas;

	if (! (room && (head || empty)) ) {
		TRACE("0x%p cannot acquire %d replicas, %d free\n",
		      greq, req->replicas, resource->free_replicas,
		      room, head, empty);
		return 0;
	}

	resource->free_replicas -= req->replicas;
	BUG_ON(resource->free_replicas > dgl->num_replicas);

	TRACE("0x%p acquired %d replicas of rid %d\n",
	      greq, req->replicas, rid);

	mask_idx(rid, &word, &bit);


	TRACE("0x%p, %lu, 0x%p\n", greq->waiting, greq->waiting[word],
	      &greq->waiting[word]);

	clear_bit(bit, &greq->waiting[word]);

        waiting = 0;
	for (word = 0; word < MASK_WORDS(dgl); word++) {
		waiting |= greq->waiting[word];
		if (waiting)
			break;
	}

	if (!waiting) {
		TRACE("0x%p acquired all resources\n", greq);
		BUG_ON(dgl->acquired[greq->cpu]);
		dgl->acquired[greq->cpu] = greq;
		litmus_reschedule(greq->cpu);
		dgl->running++;
	}

	return 1;
}

/**
 * add_group_req - initiate group request.
 */
void add_group_req(struct dgl *dgl, struct dgl_group_req *greq, int cpu)
{
	int b, w, i, succ, all_succ = 1;
	struct dgl_req *req;
	struct dgl_resource *resource;

	greq->cpu = cpu;
	greq->ts = dgl->ts++;

	TRACE("0x%p group request added for CPU %d\n", greq, cpu);
	BUG_ON(dgl->acquired[cpu] == greq);

	++dgl->requests;

	for_each_resource(greq->requested, dgl, w, b, i) {
		__set_bit(b, &greq->waiting[w]);
	}

	for_each_resource(greq->requested, dgl, w, b, i) {
		req = &greq->requests[i];
		resource = &dgl->resources[i];

		succ = try_acquire(dgl, resource, req);
		all_succ &= succ;

		if (!succ) {
			TRACE("0x%p waiting on rid %d\n", greq, i);
			list_add_tail(&req->list, &resource->waiting);
		}
	}

	/* Grant empty requests */
	if (all_succ && !dgl->acquired[cpu]) {
		TRACE("0x%p empty group request acquired cpu %d\n", greq, cpu);
		dgl->acquired[cpu] = greq;
		++dgl->running;
	}

	BUG_ON(dgl->requests && !dgl->running);
}

/**
 * remove_group_req - abandon group request.
 *
 * This will also progress the waiting queues of resources acquired by @greq.
 */
void remove_group_req(struct dgl *dgl, struct dgl_group_req *greq)
{
	int b, w, i;
	struct dgl_req *req, *next;
	struct dgl_resource *resource;

	TRACE("0x%p removing group request for CPU %d\n", greq, greq->cpu);

	--dgl->requests;

	if (dgl->acquired[greq->cpu] == greq) {
		TRACE("0x%p no longer acquired on CPU %d\n", greq, greq->cpu);
		dgl->acquired[greq->cpu] = NULL;
		--dgl->running;
	}

	for_each_resource(greq->requested, dgl, w, b, i) {
		req = &greq->requests[i];
		resource = &dgl->resources[i];

		if (!list_empty(&req->list)) {
			/* Waiting on resource */
			clear_bit(b, &greq->waiting[w]);
			list_del_init(&req->list);
			TRACE("Quitting 0x%p from rid %d\n",
			      req, i);
		} else {
			/* Have resource */
			resource->free_replicas += req->replicas;
			BUG_ON(resource->free_replicas > dgl->num_replicas);
			TRACE("0x%p releasing %d of %d replicas, rid %d\n",
			      greq, req->replicas, resource->free_replicas, i);

			if (!list_empty(&resource->waiting)) {
				/* Give it to the next guy */
				next = list_first_entry(&resource->waiting,
							struct dgl_req,
							list);

				BUG_ON(next->greq->ts < greq->ts);

				if (try_acquire(dgl, resource, next)) {
					list_del_init(&next->list);
					print_waiting(dgl, resource);

				}
			}
		}
	}

	BUG_ON(dgl->requests && !dgl->running);
}