~ubuntu-branches/ubuntu/quantal/open-vm-tools/quantal-201207201942

« back to all changes in this revision

Viewing changes to modules/linux/vmhgfs/transport.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Baumann
  • Date: 2009-10-18 12:28:19 UTC
  • mfrom: (1.1.7 upstream) (2.4.9 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091018122819-00vqew6m0ztpqcqp
Tags: 2009.10.15-201664-1
MergingĀ upstreamĀ versionĀ 2009.10.15-201664.

Show diffs side-by-side

added added

removed removed

Lines of Context:
60
60
static struct list_head hgfsRepPending;       /* Reply pending queue. */
61
61
static spinlock_t hgfsRepQueueLock;           /* Reply pending queue lock. */
62
62
 
63
 
#define HgfsRequestId(req) ((HgfsRequest *)req)->id
64
 
 
65
 
 
66
 
/*
67
 
 * Private function implementations.
68
 
 */
 
63
/*
 
64
 *----------------------------------------------------------------------
 
65
 *
 
66
 * HgfsTransportOpenChannel --
 
67
 *
 
68
 *     Opens given communication channel with HGFS server.
 
69
 *
 
70
 * Results:
 
71
 *     TRUE on success, FALSE on failure.
 
72
 *
 
73
 * Side effects:
 
74
 *     None
 
75
 *
 
76
 *----------------------------------------------------------------------
 
77
 */
 
78
 
 
79
static Bool
 
80
HgfsTransportOpenChannel(HgfsTransportChannel *channel)
 
81
{
 
82
   Bool ret;
 
83
 
 
84
   switch (channel->status) {
 
85
   case HGFS_CHANNEL_UNINITIALIZED:
 
86
   case HGFS_CHANNEL_DEAD:
 
87
      ret = FALSE;
 
88
      break;
 
89
 
 
90
   case HGFS_CHANNEL_CONNECTED:
 
91
      ret = TRUE;
 
92
      break;
 
93
 
 
94
   case HGFS_CHANNEL_NOTCONNECTED:
 
95
      ret = channel->ops.open(channel);
 
96
      if (ret) {
 
97
         channel->status = HGFS_CHANNEL_CONNECTED;
 
98
      }
 
99
      break;
 
100
 
 
101
   default:
 
102
      ret = FALSE;
 
103
      ASSERT(0); /* Not reached. */
 
104
   }
 
105
 
 
106
   return ret;
 
107
}
 
108
 
 
109
 
 
110
/*
 
111
 *----------------------------------------------------------------------
 
112
 *
 
113
 * HgfsTransportCloseChannel --
 
114
 *
 
115
 *     Closes currently open communication channel. Has to be called
 
116
 *     while holdingChannelLock.
 
117
 *
 
118
 * Results:
 
119
 *     None
 
120
 *
 
121
 * Side effects:
 
122
 *     None
 
123
 *
 
124
 *----------------------------------------------------------------------
 
125
 */
 
126
 
 
127
static void
 
128
HgfsTransportCloseChannel(HgfsTransportChannel *channel)
 
129
{
 
130
   if (channel->status == HGFS_CHANNEL_CONNECTED ||
 
131
       channel->status == HGFS_CHANNEL_DEAD) {
 
132
 
 
133
      channel->ops.close(channel);
 
134
      channel->status = HGFS_CHANNEL_NOTCONNECTED;
 
135
   }
 
136
}
 
137
 
69
138
 
70
139
/*
71
140
 *----------------------------------------------------------------------
86
155
static Bool
87
156
HgfsTransportSetupNewChannel(void)
88
157
{
89
 
   hgfsChannel = HgfsGetVSocketChannel();
90
 
   if (hgfsChannel != NULL) {
91
 
      if (hgfsChannel->ops.open(hgfsChannel)) {
92
 
         return TRUE;
93
 
      }
94
 
   }
95
 
 
96
 
   hgfsChannel = HgfsGetTcpChannel();
97
 
   if (hgfsChannel != NULL) {
98
 
      if (hgfsChannel->ops.open(hgfsChannel)) {
99
 
         return TRUE;
100
 
      }
101
 
   }
102
 
 
103
 
   hgfsChannel = HgfsGetBdChannel();
104
 
   if (hgfsChannel != NULL) {
105
 
      if (hgfsChannel->ops.open(hgfsChannel)) {
106
 
         return TRUE;
107
 
      }
108
 
   }
109
 
 
110
 
   hgfsChannel = NULL;
111
 
   return FALSE;
112
 
}
113
 
 
114
 
 
115
 
/*
116
 
 *----------------------------------------------------------------------
117
 
 *
118
 
 * HgfsTransportStopCurrentChannel --
119
 
 *
120
 
 *     Teardown current channel and stop current receive thread.
121
 
 *
122
 
 * Results:
123
 
 *     None
124
 
 *
125
 
 * Side effects:
126
 
 *     None
127
 
 *
128
 
 *----------------------------------------------------------------------
129
 
 */
130
 
 
131
 
static void
132
 
HgfsTransportStopCurrentChannel(void)
133
 
{
134
 
   if (hgfsChannel) {
135
 
      hgfsChannel->ops.exit(hgfsChannel);
136
 
      hgfsChannel = NULL;
137
 
   }
138
 
}
139
 
 
140
 
 
141
 
/*
142
 
 *----------------------------------------------------------------------
143
 
 *
144
 
 * HgfsTransportChannelFailover --
145
 
 *
146
 
 *     Called when current channel doesn't work. Find a new channel
147
 
 *     for transport.
148
 
 *
149
 
 * Results:
150
 
 *     TRUE on success, otherwise FALSE;
151
 
 *
152
 
 * Side effects:
153
 
 *     Teardown current opened channel and the receive thread, set up
154
 
 *     new channel and new receive thread.
155
 
 *
156
 
 *----------------------------------------------------------------------
157
 
 */
158
 
 
159
 
static Bool
160
 
HgfsTransportChannelFailover(void) {
161
 
   Bool ret = FALSE;
162
 
   HgfsTransportStopCurrentChannel();
163
 
   ret = HgfsTransportSetupNewChannel();
164
 
   LOG(8, ("VMware hgfs: %s result: %s.\n", __func__, ret ? "TRUE" : "FALSE"));
165
 
   return ret;
 
158
   HgfsTransportChannel *newChannel;
 
159
 
 
160
   newChannel = HgfsGetVSocketChannel();
 
161
   if (newChannel != NULL) {
 
162
      if (HgfsTransportOpenChannel(newChannel)) {
 
163
         hgfsChannel = newChannel;
 
164
         return TRUE;
 
165
      }
 
166
   }
 
167
 
 
168
   newChannel = HgfsGetTcpChannel();
 
169
   if (newChannel != NULL) {
 
170
      if (HgfsTransportOpenChannel(newChannel)) {
 
171
         hgfsChannel = newChannel;
 
172
         return TRUE;
 
173
      }
 
174
   }
 
175
 
 
176
   newChannel = HgfsGetBdChannel();
 
177
   ASSERT(newChannel);
 
178
   hgfsChannel = newChannel;
 
179
   return HgfsTransportOpenChannel(newChannel);
166
180
}
167
181
 
168
182
 
215
229
   ASSERT(req);
216
230
 
217
231
   spin_lock(&hgfsRepQueueLock);
218
 
   if (!list_empty(&req->list)) {
219
 
      list_del_init(&req->list);
220
 
   }
221
 
   spin_unlock(&hgfsRepQueueLock);
222
 
}
223
 
 
224
 
 
225
 
/*
226
 
 * Public function implementations.
227
 
 */
228
 
 
229
 
/*
230
 
 *----------------------------------------------------------------------
231
 
 *
232
 
 * HgfsTransportProcessPacket --
233
 
 *
234
 
 *     Helper function to process received packets, called by the channel
235
 
 *     handler thread.
236
 
 *
237
 
 * Results:
238
 
 *     None
239
 
 *
240
 
 * Side effects:
241
 
 *     None
242
 
 *
243
 
 *----------------------------------------------------------------------
244
 
 */
245
 
 
246
 
void
247
 
HgfsTransportProcessPacket(char *receivedPacket,    //IN: received packet
248
 
                           size_t receivedSize)     //IN: packet size
249
 
{
250
 
   struct list_head *cur, *next;
251
 
   HgfsHandle id;
252
 
   Bool found = FALSE;
253
 
 
254
 
   /* Got the reply. */
255
 
 
256
 
   ASSERT(receivedPacket != NULL && receivedSize > 0);
257
 
   id = HgfsRequestId(receivedPacket);
258
 
   LOG(8, ("VMware hgfs: %s entered.\n", __func__));
259
 
   LOG(6, (KERN_DEBUG "VMware hgfs: %s: req id: %d\n", __func__, id));
 
232
   list_del_init(&req->list);
 
233
   spin_unlock(&hgfsRepQueueLock);
 
234
}
 
235
 
 
236
 
 
237
/*
 
238
 *----------------------------------------------------------------------
 
239
 *
 
240
 * HgfsTransportFlushPendingRequests --
 
241
 *
 
242
 *     Complete all submitted requests with an error, called when
 
243
 *     we are about to tear down communication channel.
 
244
 *
 
245
 * Results:
 
246
 *     None
 
247
 *
 
248
 * Side effects:
 
249
 *     None
 
250
 *
 
251
 *----------------------------------------------------------------------
 
252
 */
 
253
 
 
254
static void
 
255
HgfsTransportFlushPendingRequests(void)
 
256
{
 
257
   struct HgfsReq *req;
 
258
 
 
259
   spin_lock(&hgfsRepQueueLock);
 
260
 
 
261
   list_for_each_entry(req, &hgfsRepPending, list) {
 
262
      if (req->state == HGFS_REQ_STATE_SUBMITTED) {
 
263
         LOG(6, ("VMware hgfs: %s: injecting error reply to req id: %d\n",
 
264
                 __func__, req->id));
 
265
         HgfsFailReq(req, -EIO);
 
266
      }
 
267
   }
 
268
 
 
269
   spin_unlock(&hgfsRepQueueLock);
 
270
}
 
271
 
 
272
/*
 
273
 *----------------------------------------------------------------------
 
274
 *
 
275
 * HgfsTransportGetPendingRequest --
 
276
 *
 
277
 *     Attempts to locate request with specified ID in the queue of
 
278
 *     pending (waiting for server's reply) requests.
 
279
 *
 
280
 * Results:
 
281
 *     NULL if request not found; otherwise address of the request
 
282
 *     structure.
 
283
 *
 
284
 * Side effects:
 
285
 *     Increments reference count of the request.
 
286
 *
 
287
 *----------------------------------------------------------------------
 
288
 */
 
289
 
 
290
HgfsReq *
 
291
HgfsTransportGetPendingRequest(HgfsHandle id)   // IN: id of the request
 
292
{
 
293
   HgfsReq *cur, *req = NULL;
 
294
 
 
295
   spin_lock(&hgfsRepQueueLock);
 
296
 
 
297
   list_for_each_entry(cur, &hgfsRepPending, list) {
 
298
      if (cur->id == id) {
 
299
         ASSERT(cur->state == HGFS_REQ_STATE_SUBMITTED);
 
300
         req = HgfsRequestGetRef(cur);
 
301
         break;
 
302
      }
 
303
   }
 
304
 
 
305
   spin_unlock(&hgfsRepQueueLock);
 
306
 
 
307
   return req;
 
308
}
 
309
 
 
310
 
 
311
/*
 
312
 *----------------------------------------------------------------------
 
313
 *
 
314
 * HgfsTransportAllocateRequest --
 
315
 *
 
316
 *     Allocates HGFS request structre using channel-specific allocator.
 
317
 *
 
318
 * Results:
 
319
 *     NULL on failure; otherwisepointer to newly allocated request.
 
320
 *
 
321
 * Side effects:
 
322
 *     None
 
323
 *
 
324
 *----------------------------------------------------------------------
 
325
 */
 
326
 
 
327
HgfsReq *
 
328
HgfsTransportAllocateRequest(size_t bufferSize)   // IN: size of the buffer
 
329
{
 
330
   HgfsReq *req = NULL;
260
331
   /*
261
 
    * Search through hgfsRepPending queue for the matching id and wake up
262
 
    * the associated waiting process. Delete the req from the queue.
 
332
    * We use a temporary variable to make sure we stamp the request with
 
333
    * same channel as we used to make allocation since hgfsChannel can
 
334
    * be changed while we do allocation.
263
335
    */
264
 
   spin_lock(&hgfsRepQueueLock);
265
 
   list_for_each_safe(cur, next, &hgfsRepPending) {
266
 
      HgfsReq *req;
267
 
      req = list_entry(cur, HgfsReq, list);
268
 
      if (req->id == id) {
269
 
         ASSERT(req->state == HGFS_REQ_STATE_SUBMITTED);
270
 
         HgfsCompleteReq(req, receivedPacket, receivedSize);
271
 
         found = TRUE;
272
 
         break;
273
 
      }
274
 
   }
275
 
   spin_unlock(&hgfsRepQueueLock);
276
 
 
277
 
   if (!found) {
278
 
      LOG(4, ("VMware hgfs: %s: No matching id, dropping reply\n",
279
 
              __func__));
280
 
   }
281
 
   LOG(8, ("VMware hgfs: %s exited.\n", __func__));
282
 
}
283
 
 
284
 
 
285
 
/*
286
 
 *----------------------------------------------------------------------
287
 
 *
288
 
 * HgfsTransportBeforeExitingRecvThread --
289
 
 *
290
 
 *     The cleanup work to do before the recv thread exits, including
291
 
 *     completing pending requests with error.
292
 
 *
293
 
 * Results:
294
 
 *     None
295
 
 *
296
 
 * Side effects:
297
 
 *     None
298
 
 *
299
 
 *----------------------------------------------------------------------
300
 
 */
301
 
 
302
 
void
303
 
HgfsTransportBeforeExitingRecvThread(void)
304
 
{
305
 
   struct list_head *cur, *next;
306
 
 
307
 
   /* Walk through hgfsRepPending queue and reply them with error. */
308
 
   spin_lock(&hgfsRepQueueLock);
309
 
   list_for_each_safe(cur, next, &hgfsRepPending) {
310
 
      HgfsReq *req;
311
 
      HgfsReply reply;
312
 
 
313
 
      /* XXX: Make the request senders be aware of this error. */
314
 
      reply.status = -EIO;
315
 
      req = list_entry(cur, HgfsReq, list);
316
 
      LOG(6, ("VMware hgfs: %s: injecting error reply to req id: %d\n",
317
 
              __func__, req->id));
318
 
      HgfsCompleteReq(req, (char *)&reply, sizeof reply);
319
 
   }
320
 
   spin_unlock(&hgfsRepQueueLock);
 
336
   HgfsTransportChannel *currentChannel = hgfsChannel;
 
337
 
 
338
   ASSERT(currentChannel);
 
339
 
 
340
   req = currentChannel->ops.allocate(bufferSize);
 
341
   if (req) {
 
342
         req->transportId = currentChannel;
 
343
   }
 
344
 
 
345
   return req;
321
346
}
322
347
 
323
348
 
340
365
int
341
366
HgfsTransportSendRequest(HgfsReq *req)   // IN: Request to send
342
367
{
343
 
   int ret;
 
368
   HgfsReq *origReq = req;
 
369
   int ret = -EIO;
 
370
 
344
371
   ASSERT(req);
345
372
   ASSERT(req->state == HGFS_REQ_STATE_UNSENT);
346
373
   ASSERT(req->payloadSize <= HGFS_PACKET_MAX);
347
374
 
348
375
   compat_mutex_lock(&hgfsChannelLock);
349
376
 
350
 
   /* Try opening the channel. */
351
 
   if (!hgfsChannel && !HgfsTransportSetupNewChannel()) {
352
 
      compat_mutex_unlock(&hgfsChannelLock);
353
 
      return -EPROTO;
354
 
   }
355
 
 
356
 
   ASSERT(hgfsChannel->ops.send);
357
 
 
358
377
   HgfsTransportAddPendingRequest(req);
359
378
 
360
 
   while ((ret = hgfsChannel->ops.send(hgfsChannel, req)) != 0) {
361
 
      LOG(4, (KERN_DEBUG "VMware hgfs: %s: send failed. Return %d\n",
 
379
   do {
 
380
 
 
381
      if (unlikely(hgfsChannel->status != HGFS_CHANNEL_CONNECTED)) {
 
382
         if (hgfsChannel->status == HGFS_CHANNEL_DEAD) {
 
383
            HgfsTransportCloseChannel(hgfsChannel);
 
384
            HgfsTransportFlushPendingRequests();
 
385
         }
 
386
 
 
387
         if (!HgfsTransportSetupNewChannel()) {
 
388
            ret = -EIO;
 
389
            goto out;
 
390
         }
 
391
      }
 
392
 
 
393
      ASSERT(hgfsChannel->ops.send);
 
394
 
 
395
      /* If channel changed since we created request we need to adjust */
 
396
      if (req->transportId != hgfsChannel) {
 
397
 
 
398
         HgfsTransportRemovePendingRequest(req);
 
399
 
 
400
         if (req != origReq) {
 
401
            HgfsRequestPutRef(req);
 
402
         }
 
403
 
 
404
         req = HgfsCopyRequest(origReq);
 
405
         if (req == NULL) {
 
406
            req = origReq;
 
407
            ret = -ENOMEM;
 
408
            goto out;
 
409
         }
 
410
 
 
411
         HgfsTransportAddPendingRequest(req);
 
412
      }
 
413
 
 
414
      ret = hgfsChannel->ops.send(hgfsChannel, req);
 
415
      if (likely(ret == 0))
 
416
         break;
 
417
 
 
418
      LOG(4, (KERN_DEBUG "VMware hgfs: %s: send failed with error %d\n",
362
419
              __func__, ret));
 
420
 
363
421
      if (ret == -EINTR) {
364
422
         /* Don't retry when we are interrupted by some signal. */
365
423
         goto out;
366
424
      }
367
 
      if (!hgfsChannel->ops.open(hgfsChannel) && !HgfsTransportChannelFailover()) {
368
 
         /* Can't establish a working channel, just report error. */
369
 
         ret = -EIO;
370
 
         goto out;
371
 
      }
372
 
   }
 
425
 
 
426
      hgfsChannel->status = HGFS_CHANNEL_DEAD;
 
427
 
 
428
   } while (1);
373
429
 
374
430
   ASSERT(req->state == HGFS_REQ_STATE_COMPLETED ||
375
431
          req->state == HGFS_REQ_STATE_SUBMITTED);
377
433
out:
378
434
   compat_mutex_unlock(&hgfsChannelLock);
379
435
 
380
 
   if (ret == 0) { /* Send succeeded. */
 
436
   if (likely(ret == 0)) {
 
437
      /* Send succeeded, wait for the reply */
381
438
      if (wait_event_interruptible(req->queue,
382
439
                                   req->state == HGFS_REQ_STATE_COMPLETED)) {
383
440
         ret = -EINTR; /* Interrupted by some signal. */
384
441
      }
385
 
   } /* else send failed. */
386
 
 
387
 
   if (ret < 0) {
388
 
      HgfsTransportRemovePendingRequest(req);
 
442
   }
 
443
 
 
444
   HgfsTransportRemovePendingRequest(req);
 
445
 
 
446
   /*
 
447
    * If we used a copy of request because we changed transport we
 
448
    * need to copy payload back into original request.
 
449
    */
 
450
   if (req != origReq) {
 
451
      ASSERT(req->payloadSize <= origReq->bufferSize);
 
452
      origReq->payloadSize = req->payloadSize;
 
453
      memcpy(origReq->payload, req->payload, req->payloadSize);
 
454
      HgfsRequestPutRef(req);
389
455
   }
390
456
 
391
457
   return ret;
418
484
   spin_lock_init(&hgfsRepQueueLock);
419
485
   compat_mutex_init(&hgfsChannelLock);
420
486
 
421
 
   hgfsChannel = NULL;
 
487
   compat_mutex_lock(&hgfsChannelLock);
 
488
 
 
489
   hgfsChannel = HgfsGetBdChannel();
 
490
   ASSERT(hgfsChannel);
 
491
 
 
492
   compat_mutex_unlock(&hgfsChannelLock);
 
493
}
 
494
 
 
495
 
 
496
/*
 
497
 *----------------------------------------------------------------------
 
498
 *
 
499
 * HgfsTransportMarkDead --
 
500
 *
 
501
 *     Marks current channel as dead so it can be cleaned up and
 
502
 *     fails all submitted requests.
 
503
 *
 
504
 * Results:
 
505
 *     None
 
506
 *
 
507
 * Side effects:
 
508
 *     None
 
509
 *
 
510
 *----------------------------------------------------------------------
 
511
 */
 
512
 
 
513
void
 
514
HgfsTransportMarkDead(void)
 
515
{
 
516
   LOG(8, ("VMware hgfs: %s entered.\n", __func__));
 
517
 
 
518
   compat_mutex_lock(&hgfsChannelLock);
 
519
 
 
520
   if (hgfsChannel) {
 
521
      hgfsChannel->status = HGFS_CHANNEL_DEAD;
 
522
   }
 
523
   HgfsTransportFlushPendingRequests();
 
524
 
 
525
   compat_mutex_unlock(&hgfsChannelLock);
422
526
}
423
527
 
424
528
 
442
546
HgfsTransportExit(void)
443
547
{
444
548
   LOG(8, ("VMware hgfs: %s entered.\n", __func__));
 
549
 
445
550
   compat_mutex_lock(&hgfsChannelLock);
446
 
   HgfsTransportStopCurrentChannel();
 
551
   ASSERT(hgfsChannel);
 
552
   HgfsTransportCloseChannel(hgfsChannel);
 
553
   hgfsChannel = NULL;
447
554
   compat_mutex_unlock(&hgfsChannelLock);
448
555
 
449
556
   ASSERT(list_empty(&hgfsRepPending));
450
557
   LOG(8, ("VMware hgfs: %s exited.\n", __func__));
451
558
}
 
559
 
 
560